commit a4dc407056c47606f7b3af420b3ebcb97e5a92dd Author: wehub-resource-sync Date: Mon Jul 13 12:26:32 2026 +0800 chore: import upstream snapshot with attribution diff --git a/.claude/skills/add-locale/SKILL.md b/.claude/skills/add-locale/SKILL.md new file mode 100644 index 0000000..a7f8480 --- /dev/null +++ b/.claude/skills/add-locale/SKILL.md @@ -0,0 +1,101 @@ +--- +name: add-locale +description: Adds a new locale to RunCat365. Use when adding a new language, creating a Strings.{lc}.resx file, registering a CultureInfo, or extending SupportedLanguage.cs with a new entry. +argument-hint: +--- + +# Add a New Locale to RunCat365 + +Modify 3 files to add support for `$ARGUMENTS`. French (`fr`) is used as an example. + +## Steps + +### 1. Create `RunCat365/Properties/Strings.{lc}.resx` + +Read `RunCat365/Properties/Strings.resx` for all keys and their English values. +Use `Strings.es.resx` as the structural template. + +- File name: `Strings.{lc}.resx` (e.g. `Strings.fr.resx`) +- Copy the XML header, schema, and `` blocks verbatim from `Strings.es.resx` +- Include every key from `Strings.resx` — no more, no less — with each `` translated +- Keep all attribute names, XML structure, and `` content in English; translate only `` text +- Verify every key from `Strings.resx` is present in the new file + +### 2. Edit `RunCat365/SupportedLanguage.cs` + +Keep the `SupportedLanguage` enum and every `switch` arm sorted **alphabetically by enum identifier** (e.g. `French` before `German` before `Japanese`). Insert the new language at its alphabetical position in each location below — do not append. + +Add the language to the `SupportedLanguage` enum: + +```csharp +enum SupportedLanguage +{ + English, + French, + Japanese, + Spanish, +} +``` + +Add the ISO code to `GetCurrentLanguage()` (arms ordered by enum identifier, so `"fr"` appears before `"ja"` even though the ISO codes aren't themselves alphabetical): + +```csharp +return culture.TwoLetterISOLanguageName switch +{ + "fr" => SupportedLanguage.French, + "ja" => SupportedLanguage.Japanese, + "es" => SupportedLanguage.Spanish, + _ => SupportedLanguage.English, +}; +``` + +Add the culture to `GetDefaultCultureInfo()`: + +```csharp +return language switch +{ + SupportedLanguage.French => new CultureInfo("fr-FR"), + SupportedLanguage.Japanese => new CultureInfo("ja-JP"), + SupportedLanguage.Spanish => new CultureInfo("es-ES"), + _ => new CultureInfo("en-US"), +}; +``` + +Add the font to `GetFontName()` — use `"Consolas"` for Latin-script languages: + +```csharp +return language switch +{ + SupportedLanguage.French => "Consolas", + SupportedLanguage.Japanese => "Noto Sans JP", + SupportedLanguage.Spanish => "Consolas", + _ => "Consolas", +}; +``` + +Add the full-width flag to `IsFullWidth()` — use `false` for Latin-script languages: + +```csharp +return language switch +{ + SupportedLanguage.French => false, + SupportedLanguage.Japanese => true, + SupportedLanguage.Spanish => false, + _ => false, +}; +``` + +### 3. Update `README.md` + +Add the new language to the **Installation** → **Language:** list, preserving the existing alphabetical order by English display name (e.g. `French` between `English (default)` and `German`). Match the existing two-space indentation under the `- Language:` bullet. + +## Checklist + +- [ ] Created `Strings.{lc}.resx` with all keys translated +- [ ] Added language to the `SupportedLanguage` enum +- [ ] Added ISO code to `GetCurrentLanguage()` +- [ ] Added culture to `GetDefaultCultureInfo()` +- [ ] Added font to `GetFontName()` +- [ ] Added full-width flag to `IsFullWidth()` +- [ ] Added the language to the Installation language list in `README.md` +- [ ] Built in Visual Studio and verified UI with OS language set to the target language diff --git a/.claude/skills/create-pr/SKILL.md b/.claude/skills/create-pr/SKILL.md new file mode 100644 index 0000000..b813563 --- /dev/null +++ b/.claude/skills/create-pr/SKILL.md @@ -0,0 +1,113 @@ +--- +name: create-pr +description: Creates a GitHub Pull Request for RunCat365 following the project's PR template. Use this skill whenever the user wants to open, submit, or create a PR, pull request, or wants to propose changes to the main branch. +--- + +# Create a Pull Request for RunCat365 + +Follow these steps to create a PR that conforms to the `.github/pull_request_template.md`. + +## Step 1: Gather branch information + +Run these commands in parallel to understand the changes: + +```bash +git log main..HEAD --oneline +git diff main..HEAD --stat +git diff main..HEAD +``` + +Also check the current branch name: + +```bash +git branch --show-current +``` + +## Step 2: Determine the PR type + +Based on the diff, classify the change as exactly one of: + +| Type | When to use | +| --------------- | ---------------------------------------------------------------- | +| **Bug Fix** | Corrects incorrect behavior without adding new functionality | +| **Refactoring** | Improves code structure or readability without changing behavior | +| **New Feature** | Adds new user-visible functionality | +| **Others** | Documentation, CI changes, build config, etc. | + +If unsure, ask the user before proceeding. + +## Step 3: Draft the PR body + +**IMPORTANT: The entire PR body must be written in English, regardless of the language the user is communicating in.** + +Fill in the template below. Every section must be present — do not omit any. + +```markdown +## Context of Contribution + +- [x] Bug Fix ← check only the one that applies +- [ ] Refactoring +- [ ] New Feature +- [ ] Others + +## Summary of the Proposal + + + +## Reason for the new feature + + + + +## Checklist + +- [x] This PR does not contain commits of multiple contexts. +- [x] Code follows proper indentation and naming conventions. +- [x] Implemented using only APIs that can be submitted to the Microsoft Store. +- [x] Works correctly in both dark theme and light theme. +- [x] Works correctly on any device. +``` + +**Checklist rules:** + +- All checklist items default to `[x]` (checked). +- If you have reason to believe an item may **not** hold (e.g., the change is UI-only so dark/light theme was not verifiable), leave it as `[ ]` and note the concern in the Summary. +- The "multiple contexts" item is `[x]` only if all commits on this branch belong to a single topic. If they don't, warn the user before creating the PR. + +## Step 4: Generate a concise PR title + +- Under 70 characters +- Imperative form, e.g. "Add French localization" or "Fix CPU usage spike on wake" +- Do not prefix with a tag like `feat:` or `fix:` — this repo does not use Conventional Commits + +## Step 5: Confirm with the user + +Show the user: + +- The proposed **title** +- The full **body** (rendered as markdown if possible) + +Ask: "Does this look right? Any changes before I create the PR?" + +Make requested edits, then proceed only after the user confirms. + +## Step 6: Create the PR + +```bash +gh pr create \ + --title "" \ + --body "$(cat <<'EOF' +<body> +EOF +)" \ + --base main +``` + +Return the PR URL to the user. + +## Notes + +- Always target `main` as the base branch unless the user specifies otherwise. +- Do not push the branch yourself — assume it is already pushed. If `gh pr create` fails because the branch has no upstream, run `git push -u origin HEAD` first and inform the user. +- If the branch already has an open PR, use `gh pr edit` to update it instead of creating a duplicate. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..096d622 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*.cs] +# FlatTrackBar is instantiated only from code, never placed on the WinForms designer, +# so designer-serialization metadata is unnecessary on its properties. + +[FlatTrackBar.cs] +dotnet_diagnostic.WFO1000.severity = none diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..2a9ff05 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,67 @@ +name: Bug Report +description: File a bug report. +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + We do not accept issues in languages other than English. + + - type: input + id: version + attributes: + label: "App version" + validations: + required: true + + - type: textarea + id: bug-description + attributes: + label: "Describe the bug" + description: "A clear and concise description of what the bug is." + validations: + required: true + + - type: textarea + id: steps-to-reproduce + attributes: + label: "How to reproduce" + description: "Steps to reproduce the bug." + value: | + 1. + 2. + 3. + ... + validations: + required: true + + - type: textarea + id: expected-behavior + attributes: + label: "Expected behavior" + description: "A description of what you expected to happen." + validations: + required: true + + - type: textarea + id: screenshots + attributes: + label: "Screenshots" + description: "If possible, attach screenshots." + validations: + required: false + + - type: checkboxes + id: checklist + attributes: + label: "Check List" + options: + - label: "I have read the [CONTRIBUTING.md](https://github.com/runcat-dev/RunCat365/blob/main/CONTRIBUTING.md) and agree to follow it." + required: true + - label: "Uninstalled App." + required: true + - label: "Re-launched your computer." + required: true + - label: "Checked that no similar issues already exist." + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..29bed50 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,45 @@ +name: Feature Request +description: File a feature request. +title: "[Idea]: " +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + We do not accept requests in languages other than English. + + - type: textarea + id: overview + attributes: + label: "Overview of the requested feature" + description: "Please provide a brief summary of the feature you're requesting." + validations: + required: true + + - type: textarea + id: reason + attributes: + label: "Reason for the request" + description: "Explain why this feature is needed and what motivated your request." + validations: + required: true + + - type: textarea + id: related-issues + attributes: + label: "Related Issues" + description: "Link to related issues, if applicable." + validations: + required: false + + - type: checkboxes + id: checklist + attributes: + label: "Check List" + options: + - label: "I have read the [CONTRIBUTING.md](https://github.com/runcat-dev/RunCat365/blob/main/CONTRIBUTING.md) and agree to follow it." + required: true + - label: "The proposed feature is beneficial to a wide range of users and not just a personal preference." + required: true + - label: "Checked that no similar requests already exist." + required: true diff --git a/.github/how_to_release.md b/.github/how_to_release.md new file mode 100644 index 0000000..4c052d9 --- /dev/null +++ b/.github/how_to_release.md @@ -0,0 +1,42 @@ +# How to Release a New Version to Microsoft Store + +This document is for code owners. + +## 1. Update Version Numbers + +Version numbers must be updated in **two** locations: + +1. **RunCat365/RunCat365.csproj** + - Update `<Version>X.Y.Z</Version>` (3-digit format) +2. **WapForStore/Package.appxmanifest** + - Update `Version="X.Y.Z.0"` in the `<Identity>` element (4-digit format) + +## 2. Build the App + +1. Open the solution in Visual Studio +2. Verify the build succeeds in Release configuration + +## 3. Create the Package + +1. In Visual Studio, right-click on the **WapForStore** project +2. Select **Publish** > **Create App Packages** +3. Choose **Microsoft Store as MSIX package** and sign in with your Microsoft account +4. Select the existing app "RunCat 365" +5. Configure package settings (x86, x64, arm64) +6. Generate the `.msixupload` file + +## 4. Submit to Partner Center + +1. Sign in to [Partner Center](https://partner.microsoft.com/dashboard) +2. Navigate to **Apps and games** > **RunCat 365** +3. Click **Start a new submission** +4. Update the following sections: + - **Packages**: Upload the generated `.msixupload` file + - **Store listings**: Update description/screenshots if needed + - **Release notes**: Describe changes in this version +5. Click **Submit to the Store** + +## 5. Wait for Certification + +- Certification typically takes 1-3 business days +- If issues are found, fix them and resubmit diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..c7e873c --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,28 @@ +## Context of Contribution + +<!-- Each pull request should fix only one issue or propose one feature. --> +<!-- Do not mix unrelated changes in a single PR. --> + +- [ ] Bug Fix +- [ ] Refactoring +- [ ] New Feature +- [ ] Others + +## Summary of the Proposal + +<!-- Provide a concise summary of what this pull request proposes. --> + +## Reason for the new feature + +<!-- If it's a new feature, explain why this feature is necessary. --> +<!-- Explain how important this feature is to many users. --> +<!-- Explain if the benefits of the new feature outweigh the maintenance cost. --> + +## Checklist + +- [ ] I have read the [CONTRIBUTING.md](../blob/main/CONTRIBUTING.md) and agree to follow it. +- [ ] This PR does not contain commits of multiple contexts. +- [ ] Code follows proper indentation and naming conventions. +- [ ] Implemented using only APIs that can be submitted to the Microsoft Store. +- [ ] Works correctly in both dark theme and light theme. +- [ ] Works correctly on any device. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..226bdf6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Visual Studio +.vs/ +bin/ +obj/ +[Ll]og/ +[Ll]ogs/ +.DS_Store +TestResults + +/WapForStore/AppPackages/** +!/WapForStore/AppPackages/rm-wap-for-store.sh +BundleArtifacts/ + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# Wap +WapForStore/**/*.assets.cache + +# Claude Code +.claude/* +!.claude/skills/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f10d569 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,52 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## You Must + +- Respond in the same language the user is writing in. +- Commit to Git only when instructed. +- If multiple requirements are given in a single instruction, divide the commits into appropriate sizes/granularities. +- When creating a pull request, use the `.claude/skills/create-pr` skill so the project's `.github/pull_request_template.md` is followed. + +## Project Overview + +Windows Forms application (.NET 9.0 / C#) distributed via the Microsoft Store. Runs as a system tray icon whose animation reflects system load. + +**Top-level layout:** + +- `RunCat365.sln` — Main solution +- `RunCat365/` — Main application project +- `WapForStore/` — Windows Application Packaging project for the Microsoft Store + +**Build:** Open `RunCat365.sln` in Visual Studio. Supported platforms: x64, x86, ARM64. Target framework: .NET 9.0 (Windows 10.0.26100.0). + +**Releasing — version numbers must be updated in two places:** + +1. `RunCat365/RunCat365.csproj` — `<Version>X.Y.Z</Version>` (3-digit) +2. `WapForStore/Package.appxmanifest` — `Version="X.Y.Z.0"` in `<Identity>` (4-digit) + +## Architecture (patterns, not file lists) + +- **Entry point:** `Program.cs` hosts the tray application context that owns the timers, the tray icon, and the context menu. +- **Repository pattern for system metrics:** Each metric has a `*Repository` class that produces a matching `*Info` struct (CPU / GPU / memory / storage / network / temperature). Add a new metric by adding a paired repository and info struct. +- **Animation loop:** A 1-second fetch timer refreshes the `*Info` structs; a separate animate timer advances frames at a rate derived from the `SpeedSource` setting. Theme-aware icon recoloring lives in `BitmapExtension`. +- **Custom runners:** User-defined animations are loaded from `profiles.json` and managed by the `CustomRunner*` classes; the active profile is stored in the `CustomRunnerName` setting. +- **EndlessGame:** Mini-game living in `EndlessGameForm` and its collaborator classes. +- **Settings & resources:** Persisted user preferences in `Properties/UserSettings.settings`; embedded images/icons in `Properties/Resources.resx`. + +## Localization + +- Localized strings live in `Properties/Strings.resx` (English default) and sibling `Properties/Strings.{locale}.resx` files. Any new string must be added to every variant. +- Per-locale font and culture settings live in `SupportedLanguage.cs`. +- When adding a brand-new locale (not just new strings), follow the `.claude/skills/add-locale` skill. + +## Coding Rules + +General contribution rules — indentation style, `var` usage, single-change PRs, English-only code, license, and the rest — are documented in [`CONTRIBUTING.md`](CONTRIBUTING.md). Follow those first. + +Project-specific additions: + +- Do not write comments within the source code. Use names that make intent obvious. +- Write abbreviations such as URL or ID in either all lowercase or all uppercase (do not use Upper Camel Case for them). +- Do not use abbreviations like `img` for `image` or `cnt` for `count`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8cff1e2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,189 @@ +# Contributing to RunCat365 + +Thank you for your interest in contributing to **RunCat365** 🐈 +RunCat365 is a Windows system monitoring application represented as a running cat animation. + +All kinds of contributions are welcome: bug reports, feature requests, and code contributions. + +This document describes the rules, steps, and expectations for contributing to this project. + +--- + +## Before Getting Started + +- **Only contributions in English are accepted.** Any contribution in another language may be closed. +- **This project is Windows-only.** Issues or requests related to Linux, macOS, or other platforms will not be accepted. +- Always use the provided **Issue** and **Pull Request** templates. Submissions that do not follow the templates may be closed. +- Be respectful, constructive, and professional in all interactions. + +--- + +## Table of Contents + +- [Before Getting Started](#before-getting-started) +- [Issues](#issues) + - [Bug Reports](#bug-reports) + - [Feature Requests](#feature-requests) + - [Other Issues](#other-issues) +- [Pull Requests](#pull-requests) + - [Before Opening a Pull Request](#before-opening-a-pull-request) + - [Cloning and Working on the Repository](#cloning-and-working-on-the-repository) + - [Submitting a Pull Request](#submitting-a-pull-request) +- [Code Style Guidelines](#code-style-guidelines) +- [Review Process](#review-process) + - [Community Reviews](#community-reviews) + - [Responding to Review Comments](#responding-to-review-comments) +- [Thank You](#thank-you) + +--- + +## Issues + +### Bug Reports + +To report a bug: + +1. Make sure there is no existing issue reporting the same bug. + If one exists, add any additional relevant information as a comment instead of creating a new issue. +2. Click `New issue` and select the `Bug Report` template. +3. Follow all checklist steps and confirm that the bug still occurs. +4. Provide clear and complete information. More details help maintainers resolve the issue faster. +5. Submit the issue and stay attentive, as maintainers may request additional information. + +--- + +### Feature Requests + +To suggest a new feature: + +1. Check if a similar feature request already exists. + If so, please contribute to the existing discussion instead of opening a new one. +2. Ensure your suggestion benefits a broad range of users and is not only a personal preference. +3. Click `New issue` and select the `Feature Request` template. +4. Fill out the template as clearly and completely as possible. +5. Submit the issue and remain available for follow-up questions. + +--- + +### Other Issues + +> [!IMPORTANT] +> This option is only for issues that do not fit into the categories above. +> Issues that do not use the appropriate template will be closed without notice. + +1. Click `New issue` and select `Blank issue`. +2. Describe your request clearly and in detail. +3. Submit the issue and stay attentive to maintainer feedback. + +--- + +## Pull Requests + +### Before Opening a Pull Request + +- **.NET 9.0** is required. +- All code must be written in **English**. + Use the localization system for user-facing text in other languages. + If you use Claude Code, the `.claude/skills/add-locale` skill walks through every file that needs updating when introducing a new locale. +- Use the **[Allman indentation style](https://en.wikipedia.org/wiki/Indentation_style#Allman_style)**. +- Use `var` when the type is obvious from the assignment. +- Follow the existing formatting and conventions used in the codebase. +- Keep each pull request focused on **a single change or context**. + For multiple unrelated changes, create separate pull requests. +- Keep code clean, readable, and easy to understand. +- **Review the entire diff yourself before opening a pull request.** + This applies whether the code was hand-written or generated by an AI assistant — you are responsible for every line you submit. +- This repository is licensed under **Apache-2.0**. + +--- + +### Cloning and Working on the Repository + +1. Fork the `main` branch. +2. Make sure Git is installed. +3. Clone your fork locally: + + ```bash + git clone https://github.com/your-username/RunCat365.git + cd RunCat365 + ``` +4. Create a new branch: + + ```bash + git switch -c branch-name + ``` + + Use a short, descriptive branch name. +5. Make your changes using your preferred IDE + (Visual Studio is recommended). +6. Keep functions within their respective classes. +7. Verify that the project builds and runs without errors. +8. Ensure no unnecessary or accidental changes were made. +9. Stage your changes: + + ```bash + git add . + ``` +10. Commit your changes: + + ```bash + git commit -m "Clear and descriptive commit message" + ``` +11. Push the branch: + + ```bash + git push origin branch-name + ``` + +--- + +### Submitting a Pull Request + +1. Click **New pull request**. +2. Select the branch you worked on. +3. Choose the type of contribution. +4. Fill in all requested information clearly and completely. +5. Ensure all checklist items are satisfied. +6. Submit the pull request. + +--- + +## Code Style Guidelines + +* Follow existing project conventions. +* Use the [Allman indentation style](https://en.wikipedia.org/wiki/Indentation_style#Allman_style). +* Use meaningful and descriptive names. +* Avoid unnecessary complexity. +* Prefer readable and self-explanatory code over clever solutions. + +--- + +## Review Process + +* Pull requests are reviewed as time permits. +* Not all contributions are guaranteed to be accepted. +* Maintainers may request changes before merging. +* Inactive or non-responsive pull requests may be closed. + +### Community Reviews + +* Reviews and comments from non-maintainer community members are welcome and appreciated. +* However, **only feedback from maintainers is considered official**, and only maintainers decide whether a pull request is merged. +* Community reviewers may not be fully familiar with this project's contribution guidelines, so their suggestions may not always align with project policy. +* Contributors are free to consider community feedback, but should use their own judgment and wait for maintainer review before treating any change as required. + +### Responding to Review Comments + +* **Do not click `Resolve conversation` unless you are the reviewer.** + Even after pushing a fix in response to a review comment, only the reviewer can decide whether the issue is actually resolved. + Pushing a follow-up commit and resolving the conversation yourself bypasses that judgment. +* **Address each review comment individually rather than bundling them together**, unless there is a clear reason to combine them. + The larger a single response or diff becomes, the harder it is for the reviewer to evaluate whether each point has been handled correctly. + Reply to each comment with the corresponding change so that the discussion stays focused and easy to follow. + +--- + +## Thank You + +Thank you again for contributing to **RunCat365**. +Your time and effort help make this project better for everyone 😸 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d9a10c0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md new file mode 100644 index 0000000..b11d50e --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# RunCat 365 + +**A cute running cat animation on your Windows Taskbar.** + +> [!CAUTION] +> +> - This project is for Windows, so we do not accept inquiries about macOS version. +> - We do not accept issues or pull requests in languages other than English. +> - Issues that do not follow the Issue Template will be closed without question. + +[![Issues](https://img.shields.io/github/issues/runcat-dev/RunCat365)](https://github.com/runcat-dev/RunCat365/issues) +[![Forks](https://img.shields.io/github/forks/runcat-dev/RunCat365)](https://github.com/runcat-dev/RunCat365/network/members) +[![Stars](https://img.shields.io/github/stars/runcat-dev/RunCat365)](https://github.com/runcat-dev/RunCat365/stargazers) +[![Top Language](https://img.shields.io/github/languages/top/runcat-dev/RunCat365)](https://github.com/runcat-dev/RunCat365/) +[![Releases](https://img.shields.io/github/v/release/runcat-dev/RunCat365)](https://github.com/runcat-dev/RunCat365/releases) +[![License](https://img.shields.io/github/license/runcat-dev/RunCat365)](https://github.com/runcat-dev/RunCat365/) + +`C#` `Win32` `.NET 9.0` `Visual Studio` `RunCat` + +## Demo + +<img src="./docs/images/demo.gif" width="600" height="200" alt="demo" /> +<br/> +<img src="./docs/images/overview.png" width="600" height="600" alt="overview" /> +<br/> +<img src="./docs/images/custom_runner.png" width="600" height="467" alt="overview" /> +<br/> +<img src="./docs/images/endless_game.png" width="600" height="375" alt="endless game" /> + +## Installation + +RunCat 365 is available for installation on the Microsoft Store. + +- Requirement: Windows 10 version 19041.0 or higher +- Microsoft Store: https://apps.microsoft.com/detail/9nw5lpnvwfwj +- Language: + - Chinese (simplified) + - Chinese (traditional) + - English (default) + - French + - German + - Japanese + - Korean + - Spanish + +## RunCat Developers' Community + +This is a space for RunCat contributors to communicate closely regarding development and operations. +We welcome anyone interested in contributing to RunCat. +However, please note that this is a place for discussing features, not for submitting requests. +For requests, please create an Issue according to the template. + +Portal: https://runcat-dev.github.io +Discord: https://discord.gg/wja3mmHt9z + +## Contributors + +<a href="https://github.com/runcat-dev/RunCat365/graphs/contributors"> + <img src="https://contrib.rocks/image?repo=runcat-dev/RunCat365" /> +</a> diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..999166d --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`runcat-dev/RunCat365` +- 原始仓库:https://github.com/runcat-dev/RunCat365 +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/RunCat365.sln b/RunCat365.sln new file mode 100644 index 0000000..14c818d --- /dev/null +++ b/RunCat365.sln @@ -0,0 +1,57 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36301.6 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RunCat365", "RunCat365\RunCat365.csproj", "{CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}" +EndProject +Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "WapForStore", "WapForStore\WapForStore.wapproj", "{5B865D12-1A70-4311-8EBA-010E117FE616}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Debug|ARM64.Build.0 = Debug|ARM64 + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Debug|x64.ActiveCfg = Debug|x64 + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Debug|x64.Build.0 = Debug|x64 + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Debug|x86.ActiveCfg = Debug|x86 + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Debug|x86.Build.0 = Debug|x86 + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Release|ARM64.ActiveCfg = Release|ARM64 + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Release|ARM64.Build.0 = Release|ARM64 + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Release|x64.ActiveCfg = Release|x64 + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Release|x64.Build.0 = Release|x64 + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Release|x86.ActiveCfg = Release|x86 + {CE0ABB30-977D-4DBC-8B69-EC9D6BD72000}.Release|x86.Build.0 = Release|x86 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Debug|ARM64.Build.0 = Debug|ARM64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Debug|x64.ActiveCfg = Debug|x64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Debug|x64.Build.0 = Debug|x64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Debug|x64.Deploy.0 = Debug|x64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Debug|x86.ActiveCfg = Debug|x86 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Debug|x86.Build.0 = Debug|x86 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Debug|x86.Deploy.0 = Debug|x86 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Release|ARM64.ActiveCfg = Release|ARM64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Release|ARM64.Build.0 = Release|ARM64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Release|ARM64.Deploy.0 = Release|ARM64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Release|x64.ActiveCfg = Release|x64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Release|x64.Build.0 = Release|x64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Release|x64.Deploy.0 = Release|x64 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Release|x86.ActiveCfg = Release|x86 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Release|x86.Build.0 = Release|x86 + {5B865D12-1A70-4311-8EBA-010E117FE616}.Release|x86.Deploy.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {04BCEA3C-C8B1-4028-A4AD-6C50E8210E5B} + EndGlobalSection +EndGlobal diff --git a/RunCat365/App.config b/RunCat365/App.config new file mode 100644 index 0000000..49295da --- /dev/null +++ b/RunCat365/App.config @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8" ?> +<configuration> + <configSections> + <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > + <section name="RunCat365.Properties.UserSettings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" /> + </sectionGroup> + </configSections> + <userSettings> + <RunCat365.Properties.UserSettings> + <setting name="Runner" serializeAs="String"> + <value>Cat</value> + </setting> + <setting name="Theme" serializeAs="String"> + <value /> + </setting> + <setting name="FPSMaxLimit" serializeAs="String"> + <value>FPS40</value> + </setting> + <setting name="FirstLaunch" serializeAs="String"> + <value>True</value> + </setting> + <setting name="HighScore" serializeAs="String"> + <value>0</value> + </setting> + <setting name="SpeedSource" serializeAs="String"> + <value>CPU</value> + </setting> + </RunCat365.Properties.UserSettings> + </userSettings> +</configuration> \ No newline at end of file diff --git a/RunCat365/App.manifest b/RunCat365/App.manifest new file mode 100644 index 0000000..f1da244 --- /dev/null +++ b/RunCat365/App.manifest @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> + <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> + <security> + <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> + <requestedExecutionLevel level="asInvoker" uiAccess="false" /> + </requestedPrivileges> + </security> + </trustInfo> + + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!-- Windows 10 --> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> + </application> + </compatibility> + + <application xmlns="urn:schemas-microsoft-com:asm.v3"> + <windowsSettings> + <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> + <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware> + </windowsSettings> + </application> +</assembly> diff --git a/RunCat365/BalloonTipType.cs b/RunCat365/BalloonTipType.cs new file mode 100644 index 0000000..3136151 --- /dev/null +++ b/RunCat365/BalloonTipType.cs @@ -0,0 +1,44 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; + +namespace RunCat365 +{ + internal readonly struct BalloonTipInfo(string title, string text, ToolTipIcon icon) + { + internal string Title { get; } = title; + internal string Text { get; } = text; + internal ToolTipIcon Icon { get; } = icon; + } + + internal enum BalloonTipType + { + AppLaunched, + CPUInfoUnavailable, + } + + internal static class BalloonTipTypeExtension + { + internal static BalloonTipInfo GetInfo(this BalloonTipType balloonTipType) + { + return balloonTipType switch + { + BalloonTipType.AppLaunched => new("RunCat 365", Strings.Message_AppLaunched, ToolTipIcon.Info), + BalloonTipType.CPUInfoUnavailable => new(Strings.Message_Warning, Strings.Message_CPUUsageUnavailable, ToolTipIcon.Warning), + _ => new("RunCat 365", string.Empty, ToolTipIcon.None), + }; + } + } +} diff --git a/RunCat365/BitmapExtension.cs b/RunCat365/BitmapExtension.cs new file mode 100644 index 0000000..c1f3370 --- /dev/null +++ b/RunCat365/BitmapExtension.cs @@ -0,0 +1,103 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Drawing.Imaging; + +namespace RunCat365 +{ + internal readonly ref struct BitmapLock + { + private readonly Bitmap bitmap; + internal BitmapData Data { get; } + + internal BitmapLock(Bitmap bitmap, ImageLockMode mode) + { + this.bitmap = bitmap; + Data = bitmap.LockBits( + new Rectangle(0, 0, bitmap.Width, bitmap.Height), + mode, + PixelFormat.Format32bppArgb + ); + } + + internal void Dispose() + { + bitmap.UnlockBits(Data); + } + } + + internal static class BitmapExtension + { + internal static Bitmap Recolor(this Bitmap bitmap, Color color) + { + var newBitmap = new Bitmap(bitmap.Width, bitmap.Height, PixelFormat.Format32bppArgb); + + using var srcLock = new BitmapLock(bitmap, ImageLockMode.ReadOnly); + using var dstLock = new BitmapLock(newBitmap, ImageLockMode.WriteOnly); + + unsafe + { + byte* srcPtr = (byte*)srcLock.Data.Scan0; + byte* dstPtr = (byte*)dstLock.Data.Scan0; + + for (int y = 0; y < bitmap.Height; y++) + { + byte* srcRow = srcPtr + (y * srcLock.Data.Stride); + byte* dstRow = dstPtr + (y * dstLock.Data.Stride); + + for (int x = 0; x < bitmap.Width; x++) + { + byte* srcPixel = srcRow + (x * 4); + byte* dstPixel = dstRow + (x * 4); + + dstPixel[0] = color.B; + dstPixel[1] = color.G; + dstPixel[2] = color.R; + dstPixel[3] = srcPixel[3]; + } + } + } + + return newBitmap; + } + + internal static Icon ToIcon(this Bitmap bitmap) + { + using var pngStream = new MemoryStream(); + bitmap.Save(pngStream, ImageFormat.Png); + var pngData = pngStream.ToArray(); + + using var icoStream = new MemoryStream(); + using var bw = new BinaryWriter(icoStream); + + bw.Write((short)0); + bw.Write((short)1); + bw.Write((short)1); + + bw.Write((byte)(bitmap.Width >= 256 ? 0 : bitmap.Width)); + bw.Write((byte)(bitmap.Height >= 256 ? 0 : bitmap.Height)); + bw.Write((byte)0); + bw.Write((byte)0); + bw.Write((short)1); + bw.Write((short)32); + bw.Write(pngData.Length); + bw.Write(22); + + bw.Write(pngData); + + icoStream.Position = 0; + return new Icon(icoStream); + } + } +} diff --git a/RunCat365/ByteFormatter.cs b/RunCat365/ByteFormatter.cs new file mode 100644 index 0000000..fe5c7f8 --- /dev/null +++ b/RunCat365/ByteFormatter.cs @@ -0,0 +1,32 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace RunCat365 +{ + internal static class ByteFormatter + { + internal static string ToByteFormatted(this long bytes) + { + string[] units = ["B", "KB", "MB", "GB", "TB"]; + int i = 0; + double doubleBytes = bytes; + while (1024 <= doubleBytes && i < units.Length - 1) + { + doubleBytes /= 1024; + i++; + } + return string.Format("{0:0.##} {1}", doubleBytes, units[i]); + } + } +} diff --git a/RunCat365/CPURepository.cs b/RunCat365/CPURepository.cs new file mode 100644 index 0000000..f76908e --- /dev/null +++ b/RunCat365/CPURepository.cs @@ -0,0 +1,183 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; +using System.Diagnostics; + +namespace RunCat365 +{ + struct CPUInfo + { + internal float Total { get; set; } + internal float User { get; set; } + internal float Kernel { get; set; } + internal float Idle { get; set; } + } + + internal static class CPUInfoExtension + { + internal static string GetDescription(this CPUInfo cpuInfo) + { + return $"{Strings.SystemInfo_CPU}: {cpuInfo.Total:f1}%"; + } + + internal static List<string> GenerateIndicator(this CPUInfo cpuInfo) + { + var resultLines = new List<string> + { + TreeFormatter.CreateRoot($"{Strings.SystemInfo_CPU}: {cpuInfo.Total:f1}%"), + TreeFormatter.CreateNode($"{Strings.SystemInfo_User}: {cpuInfo.User:f1}%", false), + TreeFormatter.CreateNode($"{Strings.SystemInfo_Kernel}: {cpuInfo.Kernel:f1}%", false), + TreeFormatter.CreateNode($"{Strings.SystemInfo_Available}: {cpuInfo.Idle:f1}%", true) + }; + return resultLines; + } + } + + internal class CPUPerformanceCounters + { + private const string PROCESSOR_INFORMATION_CATEGORY = "Processor Information"; + private const string PROCESSOR_CATEGORY = "Processor"; + private const string TOTAL_INSTANCE = "_Total"; + private const string PROCESSOR_UTILITY_COUNTER = "% Processor Utility"; + private const string PROCESSOR_TIME_COUNTER = "% Processor Time"; + private const string USER_TIME_COUNTER = "% User Time"; + private const string PRIVILEGED_TIME_COUNTER = "% Privileged Time"; + private const int PROCESSOR_TIME_BASED_TASK_MANAGER_MINIMUM_BUILD = 26100; + + internal PerformanceCounter Total { get; } + internal PerformanceCounter User { get; } + internal PerformanceCounter Kernel { get; } + + private CPUPerformanceCounters( + PerformanceCounter total, + PerformanceCounter user, + PerformanceCounter kernel) + { + Total = total; + User = user; + Kernel = kernel; + } + + internal static CPUPerformanceCounters? TryCreate() + { + return TryCreateFromCategory(PROCESSOR_INFORMATION_CATEGORY, TOTAL_INSTANCE) + ?? TryCreateFromCategory(PROCESSOR_CATEGORY, TOTAL_INSTANCE); + } + + private static bool TaskManagerUsesProcessorTime() + { + return PROCESSOR_TIME_BASED_TASK_MANAGER_MINIMUM_BUILD <= Environment.OSVersion.Version.Build; + } + + private static CPUPerformanceCounters? TryCreateFromCategory(string categoryName, string instanceName) + { + PerformanceCounter? total = null; + PerformanceCounter? user = null; + PerformanceCounter? kernel = null; + if (!TaskManagerUsesProcessorTime()) + { + try + { + total = new PerformanceCounter(categoryName, PROCESSOR_UTILITY_COUNTER, instanceName); + } + catch + { + total?.Close(); + total = null; + } + } + try + { + total ??= new PerformanceCounter(categoryName, PROCESSOR_TIME_COUNTER, instanceName); + user = new PerformanceCounter(categoryName, USER_TIME_COUNTER, instanceName); + kernel = new PerformanceCounter(categoryName, PRIVILEGED_TIME_COUNTER, instanceName); + _ = total.NextValue(); + _ = user.NextValue(); + _ = kernel.NextValue(); + return new CPUPerformanceCounters(total, user, kernel); + } + catch + { + total?.Close(); + user?.Close(); + kernel?.Close(); + return null; + } + } + + internal void Close() + { + Total.Close(); + User.Close(); + Kernel.Close(); + } + } + + internal class CPURepository + { + private readonly CPUPerformanceCounters? counters; + private readonly List<CPUInfo> cpuInfoList = []; + private const int CPU_INFO_LIST_LIMIT_SIZE = 5; + + internal bool IsAvailable => counters is not null; + + internal CPURepository() + { + counters = CPUPerformanceCounters.TryCreate(); + } + + internal void Update() + { + if (counters is null) return; + + var total = Math.Min(100, counters.Total.NextValue()); + var user = Math.Min(100, counters.User.NextValue()); + var kernel = Math.Min(100, counters.Kernel.NextValue()); + var idle = Math.Max(0, 100 - user - kernel); + + var cpuInfo = new CPUInfo + { + Total = total, + User = user, + Kernel = kernel, + Idle = idle, + }; + + cpuInfoList.Add(cpuInfo); + if (CPU_INFO_LIST_LIMIT_SIZE < cpuInfoList.Count) + { + cpuInfoList.RemoveAt(0); + } + } + + internal CPUInfo Get() + { + if (cpuInfoList.Count == 0) return new CPUInfo(); + + return new CPUInfo + { + Total = cpuInfoList.Average(x => x.Total), + User = cpuInfoList.Average(x => x.User), + Kernel = cpuInfoList.Average(x => x.Kernel), + Idle = cpuInfoList.Average(x => x.Idle) + }; + } + + internal void Close() + { + counters?.Close(); + } + } +} diff --git a/RunCat365/Cat.cs b/RunCat365/Cat.cs new file mode 100644 index 0000000..e7f285b --- /dev/null +++ b/RunCat365/Cat.cs @@ -0,0 +1,119 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace RunCat365 +{ + internal abstract class Cat + { + internal abstract List<int> ViolationIndices(); + internal abstract Cat Next(); + internal abstract string GetString(); + + internal class Running : Cat + { + internal Frame CurrentFrame { get; } + + internal Running(Frame frame) + { + CurrentFrame = frame; + } + + internal override List<int> ViolationIndices() + { + return CurrentFrame switch + { + Frame.Frame0 => [5, 6, 7], + Frame.Frame1 => [5, 6], + Frame.Frame2 => [5, 6], + Frame.Frame3 => [5], + Frame.Frame4 => [5, 7], + _ => [], + }; + } + + internal override Cat Next() + { + var nextFrame = (Frame)(((int)CurrentFrame + 1) % Enum.GetValues<Frame>().Length); + return new Running(nextFrame); + } + + internal override string GetString() + { + return $"running_{(int)CurrentFrame}"; + } + + internal enum Frame + { + Frame0, + Frame1, + Frame2, + Frame3, + Frame4 + } + } + + internal class Jumping : Cat + { + internal Frame CurrentFrame { get; } + + internal Jumping(Frame frame) + { + CurrentFrame = frame; + } + + internal override List<int> ViolationIndices() + { + return CurrentFrame switch + { + Frame.Frame0 => [5, 6, 7], + Frame.Frame1 => [5, 6], + Frame.Frame2 => [5, 6], + Frame.Frame3 => [5, 6], + Frame.Frame4 => [5, 6], + Frame.Frame5 => [5], + Frame.Frame6 => [], + Frame.Frame7 => [], + Frame.Frame8 => [], + Frame.Frame9 => [7], + _ => [], + }; + } + + internal override Cat Next() + { + var nextFrame = (Frame)(((int)CurrentFrame + 1) % Enum.GetValues<Frame>().Length); + return new Jumping(nextFrame); + } + + internal override string GetString() + { + return $"jumping_{(int)CurrentFrame}"; + } + + internal enum Frame + { + Frame0, + Frame1, + Frame2, + Frame3, + Frame4, + Frame5, + Frame6, + Frame7, + Frame8, + Frame9 + } + } + } +} diff --git a/RunCat365/ContextMenuManager.cs b/RunCat365/ContextMenuManager.cs new file mode 100644 index 0000000..38f5c2c --- /dev/null +++ b/RunCat365/ContextMenuManager.cs @@ -0,0 +1,508 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; +using System.ComponentModel; + +namespace RunCat365 +{ + internal class ContextMenuManager : IDisposable + { + private readonly CustomToolStripMenuItem systemInfoMenu = new(); + private readonly NotifyIcon notifyIcon = new(); + private readonly List<Icon> icons = []; + private readonly Lock iconLock = new(); + private int current = 0; + private EndlessGameForm? endlessGameForm; + private CustomRunnerForm? customRunnerForm; + private List<Bitmap>? customRunnerSourceFrames; + + internal ContextMenuManager( + Func<Runner> getRunner, + Action<Runner> setRunner, + CustomRunnerRepository customRunnerRepository, + Func<string?> getCustomRunnerName, + Action<string> applyCustomRunner, + Action<string> onCustomRunnerDeleted, + Func<Theme> getSystemTheme, + Func<Theme> getManualTheme, + Action<Theme> setManualTheme, + Func<SpeedSource> getSpeedSource, + Action<SpeedSource> setSpeedSource, + Func<SpeedSource, bool> isSpeedSourceAvailable, + Func<FPSMaxLimit> getFPSMaxLimit, + Action<FPSMaxLimit> setFPSMaxLimit, + Func<TemperatureUnit> getTemperatureUnit, + Action<TemperatureUnit> setTemperatureUnit, + Func<bool> getLaunchAtStartup, + Func<bool, bool> toggleLaunchAtStartup, + Action openProjectPage, + Action onExit + ) + { + systemInfoMenu.Text = "-\n-\n-\n-\n-"; + systemInfoMenu.Enabled = false; + + var runnersMenu = new CustomToolStripMenuItem(Strings.Menu_Runner); + runnersMenu.SetupSubMenusFromEnum<Runner>( + r => r.GetLocalizedString(), + (parent, sender, e) => + { + HandleMenuItemSelection<Runner>( + parent, + sender, + (string? s, out Runner r) => Enum.TryParse(s, out r), + r => setRunner(r) + ); + SetIcons(getSystemTheme(), getManualTheme(), getRunner()); + }, + r => getCustomRunnerName() is null && getRunner() == r, + r => GetRunnerThumbnailBitmap(getSystemTheme(), r) + ); + runnersMenu.DropDownOpening += (sender, e) => RefreshCustomRunnerMenu( + runnersMenu, + customRunnerRepository, + ResolveTheme(getSystemTheme(), getManualTheme()), + getRunner, + getCustomRunnerName, + applyCustomRunner + ); + + var themeMenu = new CustomToolStripMenuItem(Strings.Menu_Theme); + themeMenu.SetupSubMenusFromEnum<Theme>( + t => t.GetLocalizedString(), + (parent, sender, e) => + { + HandleMenuItemSelection<Theme>( + parent, + sender, + (string? s, out Theme t) => Enum.TryParse(s, out t), + t => setManualTheme(t) + ); + SetIcons(getSystemTheme(), getManualTheme(), getRunner()); + }, + t => getManualTheme() == t, + _ => null + ); + + var speedSourceMenu = new CustomToolStripMenuItem(Strings.Menu_SpeedSource); + speedSourceMenu.SetupSubMenusFromEnum<SpeedSource>( + s => s.GetLocalizedString(), + (parent, sender, e) => + { + HandleMenuItemSelection<SpeedSource>( + parent, + sender, + (string? s, out SpeedSource ss) => Enum.TryParse(s, out ss), + s => setSpeedSource(s) + ); + }, + s => getSpeedSource() == s, + _ => null, + isSpeedSourceAvailable + ); + + var fpsMaxLimitMenu = new CustomToolStripMenuItem(Strings.Menu_FPSMaxLimit); + fpsMaxLimitMenu.SetupSubMenusFromEnum<FPSMaxLimit>( + f => f.GetString(), + (parent, sender, e) => + { + HandleMenuItemSelection<FPSMaxLimit>( + parent, + sender, + (string? s, out FPSMaxLimit f) => FPSMaxLimitExtension.TryParse(s, out f), + f => setFPSMaxLimit(f) + ); + }, + f => getFPSMaxLimit() == f, + _ => null + ); + + var temperatureUnitMenu = new CustomToolStripMenuItem(Strings.Menu_TemperatureUnit); + temperatureUnitMenu.SetupSubMenusFromEnum<TemperatureUnit>( + u => u.GetLocalizedString(), + (parent, sender, e) => + { + HandleMenuItemSelection<TemperatureUnit>( + parent, + sender, + (string? s, out TemperatureUnit u) => Enum.TryParse(s, out u), + u => setTemperatureUnit(u) + ); + }, + u => getTemperatureUnit() == u, + _ => null + ); + + var launchAtStartupMenu = new CustomToolStripMenuItem(Strings.Menu_LaunchAtStartup) + { + Checked = getLaunchAtStartup() + }; + launchAtStartupMenu.Click += (sender, e) => HandleStartupMenuClick(sender, toggleLaunchAtStartup); + + var settingsMenu = new CustomToolStripMenuItem(Strings.Menu_Settings); + settingsMenu.DropDownItems.AddRange( + themeMenu, + speedSourceMenu, + fpsMaxLimitMenu, + temperatureUnitMenu, + launchAtStartupMenu + ); + + var customRunnersMenu = new CustomToolStripMenuItem(Strings.Menu_CustomRunners); + customRunnersMenu.Click += (sender, e) => ShowOrActivateCustomRunnerWindow( + customRunnerRepository, onCustomRunnerDeleted + ); + + var endlessGameMenu = new CustomToolStripMenuItem(Strings.Menu_EndlessGame); + endlessGameMenu.Click += (sender, e) => ShowOrActivateGameWindow(getSystemTheme); + + var appVersionMenu = new CustomToolStripMenuItem( + $"{Application.ProductName} v{Application.ProductVersion}" + ) + { + Enabled = false + }; + + var projectPageMenu = new CustomToolStripMenuItem(Strings.Menu_OpenProjectPage); + projectPageMenu.Click += (sender, e) => openProjectPage(); + + var informationMenu = new CustomToolStripMenuItem(Strings.Menu_Information); + informationMenu.DropDownItems.AddRange( + appVersionMenu, + projectPageMenu + ); + + var exitMenu = new CustomToolStripMenuItem(Strings.Menu_Exit); + exitMenu.Click += (sender, e) => onExit(); + + var contextMenuStrip = new ContextMenuStrip(new Container()); + contextMenuStrip.Items.AddRange( + systemInfoMenu, + new ToolStripSeparator(), + runnersMenu, + customRunnersMenu, + new ToolStripSeparator(), + settingsMenu, + informationMenu, + endlessGameMenu, + new ToolStripSeparator(), + exitMenu + ); + contextMenuStrip.Renderer = new ContextMenuRenderer(); + + SetIcons(getSystemTheme(), getManualTheme(), getRunner()); + + notifyIcon.Visible = true; + notifyIcon.ContextMenuStrip = contextMenuStrip; + } + + private static void HandleMenuItemSelection<T>( + ToolStripMenuItem parentMenu, + object? sender, + CustomTryParseDelegate<T> tryParseMethod, + Action<T> assignValueAction + ) + { + if (sender is null) return; + var item = (ToolStripMenuItem)sender; + foreach (ToolStripItem childItem in parentMenu.DropDownItems) + { + if (childItem is ToolStripMenuItem menuItem) + { + menuItem.Checked = false; + } + } + item.Checked = true; + + if (item.Tag is T tagValue) + { + assignValueAction(tagValue); + } + else if (tryParseMethod(item.Text, out T parsedValue)) + { + assignValueAction(parsedValue); + } + } + + private static Bitmap? GetRunnerThumbnailBitmap(Theme systemTheme, Runner runner) + { + var color = systemTheme.GetContrastColor(); + var iconName = $"{runner.GetString()}_0".ToLower(); + var obj = Resources.ResourceManager.GetObject(iconName); + if (obj is not Bitmap bitmap) return null; + return systemTheme == Theme.Light ? bitmap : bitmap.Recolor(color); + } + + private static void RefreshCustomRunnerMenu( + CustomToolStripMenuItem runnersMenu, + CustomRunnerRepository customRunnerRepository, + Theme theme, + Func<Runner> getRunner, + Func<string?> getCustomRunnerName, + Action<string> applyCustomRunner + ) + { + foreach (ToolStripItem item in runnersMenu.DropDownItems) + { + if (item is ToolStripMenuItem menuItem && item.Tag is Runner runner) + { + menuItem.Checked = getCustomRunnerName() is null && getRunner() == runner; + } + } + + var customItems = runnersMenu.DropDownItems + .Cast<ToolStripItem>() + .Where(item => item.Tag is CustomRunnerMenuTag) + .ToList(); + foreach (var item in customItems) + { + runnersMenu.DropDownItems.Remove(item); + if (item is ToolStripMenuItem menuItem) + { + menuItem.Image?.Dispose(); + } + item.Dispose(); + } + + var profiles = customRunnerRepository.GetAll().OrderBy(p => p.Name).ToList(); + if (profiles.Count == 0) return; + + runnersMenu.DropDownItems.Add(new ToolStripSeparator { Tag = new CustomRunnerMenuTag("") }); + foreach (var profile in profiles) + { + var name = profile.Name; + var item = new CustomToolStripMenuItem(name) + { + Tag = new CustomRunnerMenuTag(name), + Checked = string.Equals(getCustomRunnerName(), name, StringComparison.OrdinalIgnoreCase), + Image = CreateCustomRunnerThumbnail(customRunnerRepository, name, theme) + }; + item.Click += (sender, e) => applyCustomRunner(name); + runnersMenu.DropDownItems.Add(item); + } + } + + private static Bitmap? CreateCustomRunnerThumbnail(CustomRunnerRepository repository, string name, Theme theme) + { + var firstFrame = repository.LoadFirstFrame(name); + if (firstFrame is null) return null; + if (theme == Theme.Light) return firstFrame; + using (firstFrame) + { + return firstFrame.Recolor(theme.GetContrastColor()); + } + } + + internal void SetIcons(Theme systemTheme, Theme manualTheme, Runner runner) + { + ClearCustomRunnerSourceFrames(); + + var runnerName = runner.GetString(); + var rm = Resources.ResourceManager; + var capacity = runner.GetFrameNumber(); + var bitmaps = new List<Bitmap>(capacity); + for (int i = 0; i < capacity; i++) + { + var iconName = $"{runnerName}_{i}".ToLower(); + if (rm.GetObject(iconName) is Bitmap bitmap) + { + bitmaps.Add(bitmap); + } + } + ReplaceIconList(bitmaps, ResolveTheme(systemTheme, manualTheme)); + } + + internal void SetCustomIcons(List<Bitmap> frames, Theme systemTheme, Theme manualTheme) + { + ClearCustomRunnerSourceFrames(); + customRunnerSourceFrames = frames.Select(f => new Bitmap(f)).ToList(); + ReplaceIconList(customRunnerSourceFrames, ResolveTheme(systemTheme, manualTheme)); + } + + internal void RecolorActiveCustomIcons(Theme systemTheme, Theme manualTheme) + { + if (customRunnerSourceFrames is null) return; + ReplaceIconList(customRunnerSourceFrames, ResolveTheme(systemTheme, manualTheme)); + } + + internal bool HasActiveCustomIcons => customRunnerSourceFrames is not null; + + private void ReplaceIconList(IList<Bitmap> frames, Theme theme) + { + var color = theme.GetContrastColor(); + var list = new List<Icon>(frames.Count); + foreach (var frame in frames) + { + if (theme == Theme.Light) + { + list.Add(frame.ToIcon()); + } + else + { + using var recolored = frame.Recolor(color); + list.Add(recolored.ToIcon()); + } + } + + List<Icon> oldIcons; + lock (iconLock) + { + oldIcons = new List<Icon>(icons); + icons.Clear(); + icons.AddRange(list); + current = 0; + if (icons.Count > 0) + { + notifyIcon.Icon = icons[0]; + current = 1 % icons.Count; + } + } + + foreach (var icon in oldIcons) icon.Dispose(); + } + + private void ClearCustomRunnerSourceFrames() + { + if (customRunnerSourceFrames is null) return; + foreach (var bitmap in customRunnerSourceFrames) bitmap.Dispose(); + customRunnerSourceFrames = null; + } + + private static Theme ResolveTheme(Theme systemTheme, Theme manualTheme) + { + return manualTheme == Theme.System ? systemTheme : manualTheme; + } + + private static void HandleStartupMenuClick(object? sender, Func<bool, bool> toggleLaunchAtStartup) + { + if (sender is null) return; + var item = (ToolStripMenuItem)sender; + try + { + if (toggleLaunchAtStartup(item.Checked)) + { + item.Checked = !item.Checked; + } + } + catch (InvalidOperationException ex) + { + MessageBox.Show(ex.Message, Strings.Message_Warning, MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + } + + private void ShowOrActivateGameWindow(Func<Theme> getSystemTheme) + { + if (endlessGameForm is null) + { + endlessGameForm = new EndlessGameForm(getSystemTheme()); + endlessGameForm.FormClosed += (sender, e) => + { + endlessGameForm = null; + }; + endlessGameForm.Show(); + } + else + { + endlessGameForm.Activate(); + } + } + + private void ShowOrActivateCustomRunnerWindow( + CustomRunnerRepository repository, + Action<string> onCustomRunnerDeleted + ) + { + if (customRunnerForm is null) + { + customRunnerForm = new CustomRunnerForm(repository, onCustomRunnerDeleted); + customRunnerForm.FormClosed += (sender, e) => + { + customRunnerForm = null; + }; + customRunnerForm.Show(); + } + else + { + customRunnerForm.Activate(); + } + } + + internal void ShowBalloonTip(BalloonTipType balloonTipType) + { + var info = balloonTipType.GetInfo(); + notifyIcon.ShowBalloonTip(5000, info.Title, info.Text, info.Icon); + } + + internal void AdvanceFrame() + { + lock (iconLock) + { + if (icons.Count == 0) return; + if (icons.Count <= current) current = 0; + notifyIcon.Icon = icons[current]; + current = (current + 1) % icons.Count; + } + } + + internal void SetSystemInfoMenuText(string text) + { + systemInfoMenu.Text = text; + } + + internal void SetNotifyIconText(string text) + { + notifyIcon.Text = text; + } + + internal void HideNotifyIcon() + { + notifyIcon.Visible = false; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + lock (iconLock) + { + foreach (var icon in icons) icon.Dispose(); + icons.Clear(); + } + + ClearCustomRunnerSourceFrames(); + + if (notifyIcon is not null) + { + notifyIcon.ContextMenuStrip?.Dispose(); + notifyIcon.Dispose(); + } + + endlessGameForm?.Dispose(); + customRunnerForm?.Dispose(); + } + } + + private delegate bool CustomTryParseDelegate<T>(string? value, out T result); + + private sealed record CustomRunnerMenuTag(string Name); + } +} diff --git a/RunCat365/ContextMenuRenderer.cs b/RunCat365/ContextMenuRenderer.cs new file mode 100644 index 0000000..f109a9c --- /dev/null +++ b/RunCat365/ContextMenuRenderer.cs @@ -0,0 +1,40 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace RunCat365 +{ + internal class ContextMenuRenderer : ToolStripProfessionalRenderer + { + protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) + { + if (!string.IsNullOrEmpty(e.Text) && e.Item is CustomToolStripMenuItem item) + { + var textRectangle = e.TextRectangle; + textRectangle.Height = item.Bounds.Height; + TextRenderer.DrawText( + e.Graphics, + e.Text, + e.TextFont, + textRectangle, + item.ForeColor, + item.Flags() + ); + } + else + { + base.OnRenderItemText(e); + } + } + } +} diff --git a/RunCat365/CustomRunnerForm.cs b/RunCat365/CustomRunnerForm.cs new file mode 100644 index 0000000..a6cc739 --- /dev/null +++ b/RunCat365/CustomRunnerForm.cs @@ -0,0 +1,900 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; +using System.Diagnostics; + +namespace RunCat365 +{ + internal class CustomRunnerForm : Form + { + private const int PREVIEW_INITIAL_INTERVAL_MS = 100; + private const int PREVIEW_MIN_INTERVAL_MS = 20; + private const int PREVIEW_BASE_RATE = 500; + private const int RUNNER_NAME_MAX_LENGTH = 30; + + private readonly CustomRunnerRepository repository; + private readonly Action<string> onCustomRunnerDeleted; + private readonly string fontFamily; + private readonly Font tileLabelFont; + private readonly ToolTip toolTip = new(); + private ListBox runnerListBox = null!; + private TextBox nameTextBox = null!; + private FlowLayoutPanel framePanel = null!; + private Button addFramesButton = null!; + private Button removeFrameButton = null!; + private Button saveButton = null!; + private Button deleteButton = null!; + private FrameAnimationView previewView = null!; + private FlatTrackBar speedSlider = null!; + + private readonly List<Bitmap> pendingFrames = []; + private readonly List<Bitmap> recoloredFrames = []; + private int selectedFrameIndex = -1; + private Point? dragOriginPoint; + private int dragSourceIndex = -1; + + internal CustomRunnerForm( + CustomRunnerRepository repository, + Action<string> onCustomRunnerDeleted + ) + { + this.repository = repository; + this.onCustomRunnerDeleted = onCustomRunnerDeleted; + fontFamily = SupportedLanguageExtension.GetCurrentLanguage().GetFontName(); + tileLabelFont = new Font(fontFamily, 7.5F); + + InitializeFormProperties(); + InitializeControls(); + InitializeEventHandlers(); + + FitWindowToContent(); + + RefreshRunnerList(); + ValidateFormState(); + } + + private void FitWindowToContent() + { + if (Controls.Count == 0) return; + var root = Controls[0]; + root.PerformLayout(); + var preferred = root.PreferredSize; + ClientSize = new Size( + preferred.Width + Padding.Horizontal, + preferred.Height + Padding.Vertical + ); + } + + private void InitializeFormProperties() + { + Text = Strings.Window_CustomRunners; + Icon = Resources.AppIcon; + FormBorderStyle = FormBorderStyle.FixedDialog; + MaximizeBox = false; + StartPosition = FormStartPosition.CenterScreen; + BackColor = Palette.FormBackground; + ForeColor = Palette.TextPrimary; + Padding = new Padding(20); + } + + private void InitializeControls() + { + var listColumn = BuildListColumn(); + var separator = BuildVerticalSeparator(); + var editorColumn = BuildEditorColumn(); + + var root = new TableLayoutPanel + { + AutoSize = true, + AutoSizeMode = AutoSizeMode.GrowAndShrink, + ColumnCount = 3, + RowCount = 1, + BackColor = Color.Transparent + }; + root.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); + root.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); + root.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); + root.RowStyles.Add(new RowStyle(SizeType.AutoSize)); + root.Controls.Add(listColumn, 0, 0); + root.Controls.Add(separator, 1, 0); + root.Controls.Add(editorColumn, 2, 0); + + Controls.Add(root); + root.Location = new Point(Padding.Left, Padding.Top); + } + + private TableLayoutPanel BuildListColumn() + { + var listLabel = CreateLabel( + Strings.CustomRunner_AddedRunners, + 9F, FontStyle.Bold, Palette.TextSecondary + ); + listLabel.Margin = new Padding(0, 0, 0, 4); + + runnerListBox = new ListBox + { + MinimumSize = new Size(160, 200), + BackColor = Palette.ControlBackground, + ForeColor = Palette.TextPrimary, + Font = new Font(fontFamily, 9F), + BorderStyle = BorderStyle.FixedSingle, + IntegralHeight = false, + Dock = DockStyle.Fill, + Margin = new Padding(0, 0, 0, 8) + }; + + deleteButton = CreateIconColoredButton( + "×", + new Size(32, 28), + Palette.DangerRed, + Palette.DangerBorder, + Strings.CustomRunner_Delete, + initiallyEnabled: false + ); + deleteButton.Anchor = AnchorStyles.Left; + deleteButton.Margin = new Padding(0); + + var column = new TableLayoutPanel + { + AutoSize = true, + AutoSizeMode = AutoSizeMode.GrowAndShrink, + Dock = DockStyle.Fill, + ColumnCount = 1, + RowCount = 3, + BackColor = Color.Transparent + }; + column.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); + column.RowStyles.Add(new RowStyle(SizeType.AutoSize)); + column.RowStyles.Add(new RowStyle(SizeType.Percent, 100)); + column.RowStyles.Add(new RowStyle(SizeType.AutoSize)); + column.Controls.Add(listLabel, 0, 0); + column.Controls.Add(runnerListBox, 0, 1); + column.Controls.Add(deleteButton, 0, 2); + return column; + } + + private static Panel BuildVerticalSeparator() + { + return new Panel + { + Width = 1, + BackColor = Palette.BorderAccent, + Anchor = AnchorStyles.Top | AnchorStyles.Bottom, + Margin = new Padding(12, 0, 12, 0) + }; + } + + private Label CreateLabel(string text, float fontSize, FontStyle fontStyle, Color foreColor) + { + var font = new Font(fontFamily, fontSize, fontStyle); + var measured = TextRenderer.MeasureText(text, font); + return new Label + { + Text = text, + AutoSize = true, + MinimumSize = new Size(measured.Width + 2, 0), + Font = font, + ForeColor = foreColor + }; + } + + private TableLayoutPanel BuildEditorColumn() + { + var nameLabel = CreateLabel( + Strings.CustomRunner_Name, + 9F, FontStyle.Regular, Palette.TextSecondary + ); + nameLabel.Anchor = AnchorStyles.Top | AnchorStyles.Right; + nameLabel.TextAlign = ContentAlignment.MiddleRight; + nameLabel.Margin = new Padding(0, 6, 12, 8); + + nameTextBox = new TextBox + { + MinimumSize = new Size(280, 24), + BackColor = Palette.ControlBackground, + ForeColor = Palette.TextPrimary, + Font = new Font(fontFamily, 9F), + BorderStyle = BorderStyle.FixedSingle, + MaxLength = RUNNER_NAME_MAX_LENGTH, + Anchor = AnchorStyles.Left | AnchorStyles.Right, + Margin = new Padding(0, 0, 0, 8) + }; + + var requirementsCaption = CreateLabel( + Strings.CustomRunner_RequirementsLabel, + 9F, FontStyle.Regular, Palette.TextSecondary + ); + requirementsCaption.Anchor = AnchorStyles.Top | AnchorStyles.Right; + requirementsCaption.TextAlign = ContentAlignment.MiddleRight; + requirementsCaption.Margin = new Padding(0, 0, 12, 8); + + var requirementsContent = CreateLabel( + Strings.CustomRunner_Requirements, + 9F, FontStyle.Regular, Palette.TextMuted + ); + requirementsContent.Margin = new Padding(0, 0, 0, 8); + + var framesCaption = CreateLabel( + Strings.CustomRunner_FramesLabel, + 9F, FontStyle.Regular, Palette.TextSecondary + ); + framesCaption.Anchor = AnchorStyles.Top | AnchorStyles.Right; + framesCaption.TextAlign = ContentAlignment.MiddleRight; + framesCaption.Margin = new Padding(0, 0, 12, 8); + + var framesArea = BuildFramesArea(); + framesArea.Margin = new Padding(0, 0, 0, 8); + framesArea.Anchor = AnchorStyles.Left | AnchorStyles.Right; + + var previewCaption = CreateLabel( + Strings.CustomRunner_Preview, + 9F, FontStyle.Regular, Palette.TextSecondary + ); + previewCaption.Anchor = AnchorStyles.Top | AnchorStyles.Right; + previewCaption.TextAlign = ContentAlignment.MiddleRight; + previewCaption.Margin = new Padding(0, 0, 12, 8); + + var previewArea = BuildPreviewArea(); + previewArea.Margin = new Padding(0, 0, 0, 8); + previewArea.Anchor = AnchorStyles.Left | AnchorStyles.Right; + + saveButton = CreateColoredButton( + Strings.CustomRunner_Save, + new Size(85, 30), + Palette.AccentBlue, + Palette.AccentBlueBorder, + bold: true, + initiallyEnabled: false + ); + saveButton.Anchor = AnchorStyles.Right; + saveButton.Margin = new Padding(0); + + var column = new TableLayoutPanel + { + AutoSize = true, + AutoSizeMode = AutoSizeMode.GrowAndShrink, + ColumnCount = 2, + RowCount = 5, + BackColor = Color.Transparent + }; + column.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); + column.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); + for (int i = 0; i < 5; i++) + { + column.RowStyles.Add(new RowStyle(SizeType.AutoSize)); + } + + column.Controls.Add(nameLabel, 0, 0); + column.Controls.Add(nameTextBox, 1, 0); + column.Controls.Add(requirementsCaption, 0, 1); + column.Controls.Add(requirementsContent, 1, 1); + column.Controls.Add(framesCaption, 0, 2); + column.Controls.Add(framesArea, 1, 2); + column.Controls.Add(previewCaption, 0, 3); + column.Controls.Add(previewArea, 1, 3); + column.Controls.Add(saveButton, 1, 4); + return column; + } + + private TableLayoutPanel BuildFramesArea() + { + framePanel = new FlowLayoutPanel + { + MinimumSize = new Size(350, 280), + AutoScroll = true, + BackColor = Palette.FramePanelBackground, + BorderStyle = BorderStyle.FixedSingle, + WrapContents = true, + AllowDrop = true, + Anchor = AnchorStyles.Left | AnchorStyles.Right, + Margin = new Padding(0, 0, 0, 4) + }; + framePanel.DragEnter += FrameDragOver; + framePanel.DragOver += FrameDragOver; + framePanel.DragDrop += FramePanelDragDrop; + + addFramesButton = CreateIconButton( + "+", + new Size(28, 24), + Strings.CustomRunner_AddFrames + ); + addFramesButton.Margin = new Padding(0, 0, 4, 0); + + removeFrameButton = CreateIconButton( + "−", + new Size(28, 24), + Strings.CustomRunner_RemoveFrame, + initiallyEnabled: false + ); + removeFrameButton.Margin = new Padding(0); + + var buttonRow = new FlowLayoutPanel + { + AutoSize = true, + AutoSizeMode = AutoSizeMode.GrowAndShrink, + FlowDirection = FlowDirection.LeftToRight, + WrapContents = false, + BackColor = Color.Transparent, + Anchor = AnchorStyles.Left, + Margin = new Padding(0) + }; + buttonRow.Controls.Add(addFramesButton); + buttonRow.Controls.Add(removeFrameButton); + + var area = new TableLayoutPanel + { + AutoSize = true, + AutoSizeMode = AutoSizeMode.GrowAndShrink, + ColumnCount = 1, + RowCount = 2, + BackColor = Color.Transparent + }; + area.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); + area.RowStyles.Add(new RowStyle(SizeType.AutoSize)); + area.RowStyles.Add(new RowStyle(SizeType.AutoSize)); + area.Controls.Add(framePanel, 0, 0); + area.Controls.Add(buttonRow, 0, 1); + return area; + } + + private TableLayoutPanel BuildPreviewArea() + { + previewView = new FrameAnimationView + { + Size = new Size(64, 64), + BackColor = Palette.FramePanelBackground, + FrameIntervalMs = PREVIEW_INITIAL_INTERVAL_MS, + Anchor = AnchorStyles.None, + Margin = new Padding(0, 0, 12, 0) + }; + + var turtleIcon = new PictureBox + { + Size = new Size(24, 24), + SizeMode = PictureBoxSizeMode.Zoom, + Image = Resources.slider_turtle, + BackColor = Color.Transparent, + Anchor = AnchorStyles.None, + Margin = new Padding(0, 0, 8, 0) + }; + + speedSlider = new FlatTrackBar + { + Height = 24, + Minimum = 1, + Maximum = 10, + Value = 5, + Anchor = AnchorStyles.Left | AnchorStyles.Right, + Margin = new Padding(0) + }; + toolTip.SetToolTip(speedSlider, Strings.CustomRunner_PreviewSpeed); + + var rabbitIcon = new PictureBox + { + Size = new Size(24, 24), + SizeMode = PictureBoxSizeMode.Zoom, + Image = Resources.slider_rabbit, + BackColor = Color.Transparent, + Anchor = AnchorStyles.None, + Margin = new Padding(8, 0, 0, 0) + }; + + var area = new TableLayoutPanel + { + AutoSize = true, + AutoSizeMode = AutoSizeMode.GrowAndShrink, + ColumnCount = 4, + RowCount = 1, + BackColor = Color.Transparent + }; + area.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); + area.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); + area.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); + area.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); + area.RowStyles.Add(new RowStyle(SizeType.AutoSize)); + area.Controls.Add(previewView, 0, 0); + area.Controls.Add(turtleIcon, 1, 0); + area.Controls.Add(speedSlider, 2, 0); + area.Controls.Add(rabbitIcon, 3, 0); + return area; + } + + private void InitializeEventHandlers() + { + runnerListBox.SelectedIndexChanged += RunnerListBoxSelectedIndexChanged; + nameTextBox.TextChanged += (sender, e) => ValidateFormState(); + + addFramesButton.Click += AddFramesButtonClick; + removeFrameButton.Click += RemoveFrameButtonClick; + + speedSlider.ValueChanged += (sender, e) => + { + if (speedSlider.Value > 0) + { + previewView.FrameIntervalMs = Math.Max(PREVIEW_MIN_INTERVAL_MS, PREVIEW_BASE_RATE / speedSlider.Value); + } + }; + + deleteButton.Click += DeleteButtonClick; + saveButton.Click += SaveButtonClick; + } + + private ThemedButton CreateIconButton(string glyph, Size size, string tooltipText, bool initiallyEnabled = true) + { + var button = new ThemedButton + { + Text = glyph, + AutoSize = false, + Size = size, + MinimumSize = size, + Padding = new Padding(0), + FlatStyle = FlatStyle.Flat, + BackColor = Palette.ButtonBackground, + ForeColor = Palette.TextPrimary, + Font = new Font("Segoe UI", 11F, FontStyle.Regular), + Cursor = Cursors.Hand, + Enabled = initiallyEnabled + }; + button.FlatAppearance.BorderColor = Palette.BorderAccent; + toolTip.SetToolTip(button, tooltipText); + return button; + } + + private ThemedButton CreateIconColoredButton( + string glyph, + Size size, + Color backColor, + Color borderColor, + string tooltipText, + bool initiallyEnabled + ) + { + var button = new ThemedButton + { + Text = glyph, + AutoSize = false, + Size = size, + MinimumSize = size, + Padding = new Padding(0), + FlatStyle = FlatStyle.Flat, + BackColor = backColor, + ForeColor = Palette.TextPrimary, + Font = new Font("Segoe UI", 11F, FontStyle.Regular), + Cursor = Cursors.Hand, + Enabled = initiallyEnabled + }; + button.FlatAppearance.BorderColor = borderColor; + toolTip.SetToolTip(button, tooltipText); + return button; + } + + private ThemedButton CreateColoredButton( + string text, + Size minimumSize, + Color backColor, + Color borderColor, + bool bold, + bool initiallyEnabled + ) + { + var button = new ThemedButton + { + Text = text, + AutoSize = true, + AutoSizeMode = AutoSizeMode.GrowAndShrink, + MinimumSize = minimumSize, + Padding = new Padding(14, 4, 14, 4), + FlatStyle = FlatStyle.Flat, + BackColor = backColor, + ForeColor = Palette.TextPrimary, + Font = new Font(fontFamily, bold ? 9F : 8.5F, bold ? FontStyle.Bold : FontStyle.Regular), + Cursor = Cursors.Hand, + Enabled = initiallyEnabled + }; + button.FlatAppearance.BorderColor = borderColor; + return button; + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + base.OnFormClosing(e); + ClearPendingFrames(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + foreach (var frame in pendingFrames) frame.Dispose(); + pendingFrames.Clear(); + foreach (var frame in recoloredFrames) frame.Dispose(); + recoloredFrames.Clear(); + tileLabelFont.Dispose(); + toolTip.Dispose(); + } + base.Dispose(disposing); + } + + private void RefreshRunnerList() + { + runnerListBox.Items.Clear(); + foreach (var profile in repository.GetAll()) + { + runnerListBox.Items.Add(profile.Name); + } + UpdateRunnerActionButtons(); + } + + private void RunnerListBoxSelectedIndexChanged(object? sender, EventArgs e) + { + var hasSelection = runnerListBox.SelectedIndex >= 0; + UpdateRunnerActionButtons(); + + if (hasSelection && runnerListBox.SelectedItem is string selectedName) + { + nameTextBox.Text = selectedName; + ClearPendingFrames(); + var frames = repository.LoadFrames(selectedName); + pendingFrames.AddRange(frames); + selectedFrameIndex = pendingFrames.Count > 0 ? 0 : -1; + OnFramesChanged(); + } + } + + private void AddFramesButtonClick(object? sender, EventArgs e) + { + using var dialog = new OpenFileDialog + { + Filter = "PNG Images|*.png", + Multiselect = true, + Title = Strings.CustomRunner_AddFrames + }; + + if (dialog.ShowDialog() != DialogResult.OK) return; + + var sortedFiles = dialog.FileNames.OrderBy(f => f).ToArray(); + foreach (var file in sortedFiles) + { + if (pendingFrames.Count >= CustomRunnerRepository.MAX_FRAME_COUNT) break; + try + { + pendingFrames.Add(new Bitmap(file)); + } + catch (Exception ex) when (ex is OutOfMemoryException or ArgumentException or FileNotFoundException) + { + Debug.WriteLine($"Failed to load frame '{file}': {ex.Message}"); + } + } + OnFramesChanged(); + } + + private void RemoveFrameButtonClick(object? sender, EventArgs e) + { + if (selectedFrameIndex < 0 || pendingFrames.Count <= selectedFrameIndex) return; + + pendingFrames[selectedFrameIndex].Dispose(); + pendingFrames.RemoveAt(selectedFrameIndex); + selectedFrameIndex = pendingFrames.Count == 0 + ? -1 + : Math.Min(selectedFrameIndex, pendingFrames.Count - 1); + OnFramesChanged(); + } + + private void OnFramesChanged() + { + RebuildRecoloredFrames(); + RebuildFrameGrid(); + RestartPreview(); + ValidateFormState(); + } + + private void RebuildRecoloredFrames() + { + foreach (var frame in recoloredFrames) frame.Dispose(); + recoloredFrames.Clear(); + foreach (var frame in pendingFrames) + { + recoloredFrames.Add(frame.Recolor(Color.White)); + } + if (pendingFrames.Count <= selectedFrameIndex) + { + selectedFrameIndex = pendingFrames.Count - 1; + } + } + + private void RebuildFrameGrid() + { + framePanel.SuspendLayout(); + foreach (Control oldTile in framePanel.Controls) + { + oldTile.Dispose(); + } + framePanel.Controls.Clear(); + + for (int i = 0; i < recoloredFrames.Count; i++) + { + framePanel.Controls.Add(CreateFrameTile(i)); + } + + framePanel.ResumeLayout(); + } + + private Panel CreateFrameTile(int frameIndex) + { + var container = new Panel + { + Size = new Size(70, 76), + Margin = new Padding(4), + BackColor = frameIndex == selectedFrameIndex ? Palette.SelectionHighlight : Color.Transparent, + Cursor = Cursors.SizeAll + }; + + var pictureBox = new PictureBox + { + Size = new Size(48, 48), + Location = new Point(11, 0), + SizeMode = PictureBoxSizeMode.Zoom, + Image = recoloredFrames[frameIndex], + BackColor = Palette.FrameThumbnailBackground, + Cursor = Cursors.SizeAll + }; + + var indexLabel = new Label + { + Text = $"{frameIndex}", + Size = new Size(70, 18), + Location = new Point(0, 52), + TextAlign = ContentAlignment.TopCenter, + ForeColor = Palette.TextThumbnailIndex, + Font = tileLabelFont, + Cursor = Cursors.SizeAll + }; + + foreach (var control in new Control[] { container, pictureBox, indexLabel }) + { + control.Click += (sender, e) => SelectFrame(frameIndex); + WireFrameDragSource(control, frameIndex); + WireFrameDragReceiver(control, frameIndex); + } + + container.Controls.Add(pictureBox); + container.Controls.Add(indexLabel); + return container; + } + + private void WireFrameDragSource(Control control, int frameIndex) + { + control.MouseDown += (sender, e) => + { + if (e.Button != MouseButtons.Left) return; + dragOriginPoint = e.Location; + dragSourceIndex = frameIndex; + }; + control.MouseMove += (sender, e) => + { + if (dragOriginPoint is null || dragSourceIndex < 0) return; + if (e.Button != MouseButtons.Left) return; + var dx = Math.Abs(e.X - dragOriginPoint.Value.X); + var dy = Math.Abs(e.Y - dragOriginPoint.Value.Y); + if (dx < SystemInformation.DragSize.Width && dy < SystemInformation.DragSize.Height) return; + var startedIndex = dragSourceIndex; + dragOriginPoint = null; + dragSourceIndex = -1; + control.DoDragDrop(startedIndex, DragDropEffects.Move); + }; + control.MouseUp += (sender, e) => + { + dragOriginPoint = null; + dragSourceIndex = -1; + }; + } + + private void WireFrameDragReceiver(Control control, int frameIndex) + { + control.AllowDrop = true; + control.DragEnter += FrameDragOver; + control.DragOver += FrameDragOver; + control.DragDrop += (sender, e) => HandleFrameDrop(e, frameIndex); + } + + private static void FrameDragOver(object? sender, DragEventArgs e) + { + e.Effect = e.Data?.GetDataPresent(typeof(int)) == true + ? DragDropEffects.Move + : DragDropEffects.None; + } + + private void HandleFrameDrop(DragEventArgs e, int targetIndex) + { + if (e.Data?.GetData(typeof(int)) is not int sourceIndex) return; + ReorderFrame(sourceIndex, targetIndex); + } + + private void FramePanelDragDrop(object? sender, DragEventArgs e) + { + if (e.Data?.GetData(typeof(int)) is not int sourceIndex) return; + ReorderFrame(sourceIndex, pendingFrames.Count); + } + + private void ReorderFrame(int sourceIndex, int targetIndex) + { + if (sourceIndex < 0 || sourceIndex >= pendingFrames.Count) return; + if (sourceIndex == targetIndex) return; + + var item = pendingFrames[sourceIndex]; + pendingFrames.RemoveAt(sourceIndex); + + var insertAt = sourceIndex < targetIndex ? targetIndex - 1 : targetIndex; + insertAt = Math.Clamp(insertAt, 0, pendingFrames.Count); + pendingFrames.Insert(insertAt, item); + + selectedFrameIndex = insertAt; + OnFramesChanged(); + } + + private void RestartPreview() + { + previewView.FrameIntervalMs = Math.Max(PREVIEW_MIN_INTERVAL_MS, PREVIEW_BASE_RATE / speedSlider.Value); + previewView.SetFrames(recoloredFrames); + } + + private void SelectFrame(int frameIndex) + { + if (frameIndex < 0 || pendingFrames.Count <= frameIndex) return; + selectedFrameIndex = frameIndex; + RebuildFrameGrid(); + UpdateFrameActionButtons(); + } + + private void ValidateFormState() + { + var name = nameTextBox.Text.Trim(); + bool hasValidName = !string.IsNullOrWhiteSpace(name); + bool hasValidFrames = pendingFrames.Count >= CustomRunnerRepository.MIN_FRAME_COUNT; + + saveButton.Enabled = hasValidName && hasValidFrames; + + UpdateFrameActionButtons(); + } + + private void UpdateFrameActionButtons() + { + var hasSelectedFrame = 0 <= selectedFrameIndex && selectedFrameIndex < pendingFrames.Count; + removeFrameButton.Enabled = hasSelectedFrame; + } + + private void UpdateRunnerActionButtons() + { + deleteButton.Enabled = runnerListBox.SelectedIndex >= 0; + } + + private void SaveButtonClick(object? sender, EventArgs e) + { + var name = nameTextBox.Text.Trim(); + if (string.IsNullOrWhiteSpace(name)) return; + if (pendingFrames.Count < CustomRunnerRepository.MIN_FRAME_COUNT) return; + + if (repository.Exists(name)) + { + var result = MessageBox.Show( + Strings.CustomRunner_ConfirmOverwrite, + Strings.Message_Warning, + MessageBoxButtons.YesNo, + MessageBoxIcon.Question + ); + if (result != DialogResult.Yes) return; + } + + if (repository.Save(name, pendingFrames)) + { + RefreshRunnerList(); + for (int i = 0; i < runnerListBox.Items.Count; i++) + { + if (runnerListBox.Items[i] is string itemName && + itemName.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + runnerListBox.SelectedIndex = i; + break; + } + } + ValidateFormState(); + } + } + + private void DeleteButtonClick(object? sender, EventArgs e) + { + if (runnerListBox.SelectedItem is not string selectedName) return; + + var result = MessageBox.Show( + string.Format(Strings.CustomRunner_ConfirmDelete, selectedName), + Strings.Message_Warning, + MessageBoxButtons.YesNo, + MessageBoxIcon.Warning + ); + if (result != DialogResult.Yes) return; + + repository.Delete(selectedName); + onCustomRunnerDeleted(selectedName); + ClearPendingFrames(); + OnFramesChanged(); + nameTextBox.Clear(); + RefreshRunnerList(); + UpdateRunnerActionButtons(); + } + + private void ClearPendingFrames() + { + previewView.Clear(); + foreach (var frame in pendingFrames) frame.Dispose(); + pendingFrames.Clear(); + foreach (var frame in recoloredFrames) frame.Dispose(); + recoloredFrames.Clear(); + selectedFrameIndex = -1; + UpdateFrameActionButtons(); + } + + private static class Palette + { + internal static readonly Color FormBackground = Color.FromArgb(45, 45, 45); + internal static readonly Color ControlBackground = Color.FromArgb(60, 60, 60); + internal static readonly Color ButtonBackground = Color.FromArgb(70, 70, 70); + internal static readonly Color BorderAccent = Color.FromArgb(100, 100, 100); + internal static readonly Color FramePanelBackground = Color.FromArgb(55, 55, 55); + internal static readonly Color FrameThumbnailBackground = Color.FromArgb(40, 40, 40); + internal static readonly Color SelectionHighlight = Color.FromArgb(75, 95, 125); + internal static readonly Color TextPrimary = Color.White; + internal static readonly Color TextSecondary = Color.FromArgb(200, 200, 200); + internal static readonly Color TextMuted = Color.FromArgb(150, 150, 150); + internal static readonly Color TextThumbnailIndex = Color.FromArgb(170, 170, 170); + internal static readonly Color DangerRed = Color.FromArgb(120, 40, 40); + internal static readonly Color DangerBorder = Color.FromArgb(150, 60, 60); + internal static readonly Color AccentBlue = Color.FromArgb(50, 90, 160); + internal static readonly Color AccentBlueBorder = Color.FromArgb(70, 120, 200); + } + + private sealed class ThemedButton : Button + { + private static readonly Color DisabledBackColor = Color.FromArgb(58, 58, 58); + private static readonly Color DisabledForeColor = Color.FromArgb(145, 145, 145); + private static readonly Color DisabledBorderColor = Color.FromArgb(82, 82, 82); + + protected override void OnEnabledChanged(EventArgs e) + { + base.OnEnabledChanged(e); + Cursor = Enabled ? Cursors.Hand : Cursors.Default; + Invalidate(); + } + + protected override void OnPaint(PaintEventArgs e) + { + var backColor = Enabled ? BackColor : DisabledBackColor; + var foreColor = Enabled ? ForeColor : DisabledForeColor; + var borderColor = Enabled ? FlatAppearance.BorderColor : DisabledBorderColor; + + using var backBrush = new SolidBrush(backColor); + using var borderPen = new Pen(borderColor); + e.Graphics.FillRectangle(backBrush, ClientRectangle); + e.Graphics.DrawRectangle(borderPen, 0, 0, Width - 1, Height - 1); + + var textSize = TextRenderer.MeasureText(e.Graphics, Text, Font, ClientSize, TextFormatFlags.NoPadding); + var location = new Point( + (Width - textSize.Width) / 2, + (Height - textSize.Height) / 2 + ); + TextRenderer.DrawText(e.Graphics, Text, Font, location, foreColor, TextFormatFlags.NoPadding); + } + } + } +} diff --git a/RunCat365/CustomRunnerProfile.cs b/RunCat365/CustomRunnerProfile.cs new file mode 100644 index 0000000..c99a79e --- /dev/null +++ b/RunCat365/CustomRunnerProfile.cs @@ -0,0 +1,27 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Text.Json.Serialization; + +namespace RunCat365 +{ + internal class CustomRunnerProfile + { + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + [JsonPropertyName("frameFileNames")] + public List<string> FrameFileNames { get; set; } = []; + } +} diff --git a/RunCat365/CustomRunnerRepository.cs b/RunCat365/CustomRunnerRepository.cs new file mode 100644 index 0000000..cbcbf58 --- /dev/null +++ b/RunCat365/CustomRunnerRepository.cs @@ -0,0 +1,233 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Diagnostics; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Text.Json; + +namespace RunCat365 +{ + internal class CustomRunnerRepository + { + internal const int MIN_FRAME_COUNT = 2; + internal const int MAX_FRAME_COUNT = 30; + private const int MAX_FRAME_HEIGHT = 32; + private const int MIN_FRAME_WIDTH = 10; + private const int MAX_FRAME_WIDTH = 32; + private const string PROFILES_FILE_NAME = "profiles.json"; + + private readonly string basePath; + private List<CustomRunnerProfile> profiles = []; + + internal CustomRunnerRepository() + { + basePath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "RunCat365", + "CustomRunners" + ); + Directory.CreateDirectory(basePath); + Load(); + } + + internal List<CustomRunnerProfile> GetAll() + { + return [.. profiles]; + } + + internal CustomRunnerProfile? GetByName(string name) + { + return profiles.Find(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); + } + + internal List<Bitmap> LoadFrames(string name) + { + var profile = GetByName(name); + if (profile is null) return []; + + var runnerDirectory = Path.Combine(basePath, SanitizeDirectoryName(name)); + var frames = new List<Bitmap>(); + foreach (var fileName in profile.FrameFileNames) + { + var filePath = Path.Combine(runnerDirectory, fileName); + if (!File.Exists(filePath)) continue; + var frame = TryLoadBitmap(filePath); + if (frame is not null) frames.Add(frame); + } + return frames; + } + + internal Bitmap? LoadFirstFrame(string name) + { + var profile = GetByName(name); + if (profile is null || profile.FrameFileNames.Count == 0) return null; + var runnerDirectory = Path.Combine(basePath, SanitizeDirectoryName(name)); + var filePath = Path.Combine(runnerDirectory, profile.FrameFileNames[0]); + return File.Exists(filePath) ? TryLoadBitmap(filePath) : null; + } + + private static Bitmap? TryLoadBitmap(string filePath) + { + try + { + return new Bitmap(filePath); + } + catch (Exception ex) when (ex is ArgumentException or OutOfMemoryException or FileNotFoundException) + { + Debug.WriteLine($"Failed to load custom runner frame '{filePath}': {ex.Message}"); + return null; + } + } + + internal bool Save(string name, List<Bitmap> frames) + { + if (string.IsNullOrWhiteSpace(name)) return false; + if (frames.Count < MIN_FRAME_COUNT || frames.Count > MAX_FRAME_COUNT) return false; + + var runnerDirectory = Path.Combine(basePath, SanitizeDirectoryName(name)); + Directory.CreateDirectory(runnerDirectory); + + var existingProfile = GetByName(name); + if (existingProfile is not null) + { + DeleteFrameFiles(existingProfile); + profiles.Remove(existingProfile); + } + + var profile = new CustomRunnerProfile { Name = name }; + for (int i = 0; i < frames.Count; i++) + { + using var resized = ResizeFrame(frames[i]); + var fileName = $"frame_{i}.png"; + var filePath = Path.Combine(runnerDirectory, fileName); + resized.Save(filePath, ImageFormat.Png); + profile.FrameFileNames.Add(fileName); + } + + profiles.Add(profile); + return TrySaveProfiles(); + } + + internal bool Delete(string name) + { + var profile = GetByName(name); + if (profile is null) return false; + + DeleteFrameFiles(profile); + var runnerDirectory = Path.Combine(basePath, SanitizeDirectoryName(name)); + try + { + if (Directory.Exists(runnerDirectory)) + { + Directory.Delete(runnerDirectory, true); + } + } + catch (Exception ex) when (ex is IOException or UnauthorizedAccessException) + { + Debug.WriteLine($"Failed to delete runner directory '{runnerDirectory}': {ex.Message}"); + } + + profiles.Remove(profile); + TrySaveProfiles(); + return true; + } + + internal bool Exists(string name) + { + return GetByName(name) is not null; + } + + private void Load() + { + var profilesPath = Path.Combine(basePath, PROFILES_FILE_NAME); + if (!File.Exists(profilesPath)) + { + profiles = []; + return; + } + try + { + var json = File.ReadAllText(profilesPath); + profiles = JsonSerializer.Deserialize<List<CustomRunnerProfile>>(json) ?? []; + } + catch (Exception ex) when (ex is JsonException or IOException or UnauthorizedAccessException) + { + Debug.WriteLine($"Failed to load custom runner profiles: {ex.Message}"); + profiles = []; + } + } + + private bool TrySaveProfiles() + { + var profilesPath = Path.Combine(basePath, PROFILES_FILE_NAME); + try + { + var options = new JsonSerializerOptions { WriteIndented = true }; + var json = JsonSerializer.Serialize(profiles, options); + File.WriteAllText(profilesPath, json); + return true; + } + catch (Exception ex) when (ex is IOException or UnauthorizedAccessException) + { + Debug.WriteLine($"Failed to write custom runner profiles: {ex.Message}"); + return false; + } + } + + private void DeleteFrameFiles(CustomRunnerProfile profile) + { + var runnerDirectory = Path.Combine(basePath, SanitizeDirectoryName(profile.Name)); + foreach (var fileName in profile.FrameFileNames) + { + var filePath = Path.Combine(runnerDirectory, fileName); + try + { + File.Delete(filePath); + } + catch (Exception ex) when (ex is IOException or UnauthorizedAccessException) + { + Debug.WriteLine($"Failed to delete frame file '{filePath}': {ex.Message}"); + } + } + } + + private static Bitmap ResizeFrame(Bitmap original) + { + if (original.Height <= MAX_FRAME_HEIGHT && + original.Width >= MIN_FRAME_WIDTH && + original.Width <= MAX_FRAME_WIDTH) + { + return new Bitmap(original); + } + + var scale = (float)MAX_FRAME_HEIGHT / original.Height; + var newWidth = Math.Clamp((int)(original.Width * scale), MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); + var newHeight = MAX_FRAME_HEIGHT; + + var resized = new Bitmap(newWidth, newHeight); + using var graphics = Graphics.FromImage(resized); + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.DrawImage(original, 0, 0, newWidth, newHeight); + return resized; + } + + private static string SanitizeDirectoryName(string name) + { + var invalidChars = Path.GetInvalidFileNameChars(); + var sanitized = new string(name.Select(c => invalidChars.Contains(c) ? '_' : c).ToArray()); + return sanitized.ToLowerInvariant(); + } + } +} diff --git a/RunCat365/CustomToolStripMenuItem.cs b/RunCat365/CustomToolStripMenuItem.cs new file mode 100644 index 0000000..f87a794 --- /dev/null +++ b/RunCat365/CustomToolStripMenuItem.cs @@ -0,0 +1,110 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace RunCat365 +{ + internal class CustomToolStripMenuItem : ToolStripMenuItem + { + private static readonly Font MenuFont = new( + SupportedLanguageExtension.GetCurrentLanguage().GetFontName(), + 9F, + FontStyle.Regular + ); + + internal CustomToolStripMenuItem() : base() + { + Font = MenuFont; + } + + internal CustomToolStripMenuItem(string? text) : base(text) + { + Font = MenuFont; + } + + private CustomToolStripMenuItem(string? text, Image? image, object? tag, bool isChecked, EventHandler? onClick) : base(text, image, onClick) + { + Tag = tag; + Checked = isChecked; + Font = MenuFont; + } + + private readonly TextFormatFlags multiLineTextFlags = + TextFormatFlags.LeftAndRightPadding | + TextFormatFlags.VerticalCenter | + TextFormatFlags.WordBreak | + TextFormatFlags.TextBoxControl; + + private readonly TextFormatFlags singleLineTextFlags = + TextFormatFlags.LeftAndRightPadding | + TextFormatFlags.VerticalCenter | + TextFormatFlags.EndEllipsis; + + public override Size GetPreferredSize(Size constrainingSize) + { + Size baseSize = base.GetPreferredSize(constrainingSize); + if (string.IsNullOrEmpty(Text)) + { + return new Size(baseSize.Width, 22); + } + var textRenderWidth = Math.Max(constrainingSize.Width - 20, 1); + + SizeF measuredSize = TextRenderer.MeasureText( + Text, + Font, + new Size(textRenderWidth, int.MaxValue), + Flags() + ); + var calculatedHeight = (int)Math.Ceiling(measuredSize.Height) + 4; + var height = IsSingleLine() ? calculatedHeight : Math.Max(baseSize.Height, calculatedHeight); + return new Size(baseSize.Width, height); + } + + internal bool IsSingleLine() + { + return string.IsNullOrEmpty(Text) || !Text.Contains('\n'); + } + + internal TextFormatFlags Flags() + { + return IsSingleLine() ? singleLineTextFlags : multiLineTextFlags; + } + + internal void SetupSubMenusFromEnum<T>( + Func<T, string> getTitle, + Action<CustomToolStripMenuItem, object?, EventArgs> onClick, + Func<T, bool> isChecked, + Func<Runner, Bitmap?> getRunnerThumbnailBitmap, + Func<T, bool>? isVisible = null + ) where T : Enum + { + isVisible ??= _ => true; + var items = new List<CustomToolStripMenuItem>(); + foreach (T value in Enum.GetValues(typeof(T))) + { + if (!isVisible(value)) continue; + var entityName = getTitle(value); + var iconImage = value is Runner runner ? getRunnerThumbnailBitmap(runner) : null; + var item = new CustomToolStripMenuItem( + entityName, + iconImage, + value, + isChecked(value), + (sender, e) => onClick(this, sender, e) + ); + items.Add(item); + } + DropDownItems.AddRange([.. items]); + } + } +} diff --git a/RunCat365/EndlessGameForm.cs b/RunCat365/EndlessGameForm.cs new file mode 100644 index 0000000..2e640a2 --- /dev/null +++ b/RunCat365/EndlessGameForm.cs @@ -0,0 +1,318 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; +using System.Collections; +using System.Globalization; +using System.Text.RegularExpressions; +using FormsTimer = System.Windows.Forms.Timer; + +namespace RunCat365 +{ + internal class EndlessGameForm : Form + { + private const int JUMP_THRESHOLD = 17; + private readonly FormsTimer timer; + private readonly Theme systemTheme; + private GameStatus status = GameStatus.NewGame; + private Cat cat = new Cat.Running(Cat.Running.Frame.Frame0); + private readonly List<Road> roads = []; + private readonly Dictionary<string, Bitmap> catIcons = []; + private readonly Dictionary<string, Bitmap> roadIcons = []; + private int counter = 0; + private int limit = 5; + private int score = 0; + private int highScore = UserSettings.Default.HighScore; + private bool isJumpRequested = false; + private readonly bool isAutoPlay = false; + + internal EndlessGameForm(Theme systemTheme) + { + this.systemTheme = systemTheme; + + DoubleBuffered = true; + ClientSize = new Size(600, 250); + FormBorderStyle = FormBorderStyle.FixedDialog; + MaximizeBox = false; + StartPosition = FormStartPosition.CenterScreen; + Text = Strings.Window_EndlessGame; + Icon = Resources.AppIcon; + BackColor = systemTheme == Theme.Light ? Color.Gainsboro : Color.Gray; + + var rm = Resources.ResourceManager; + var rs = rm.GetResourceSet(CultureInfo.CurrentUICulture, true, true); + if (rs is not null) + { + var catRegex = new Regex(@"^cat_.*_.*$"); + var color = systemTheme.GetContrastColor(); + foreach (DictionaryEntry entry in rs) + { + var key = entry.Key.ToString(); + if (string.IsNullOrEmpty(key)) continue; + + if (catRegex.IsMatch(key)) + { + if (entry.Value is Bitmap icon) + { + catIcons.Add(key, systemTheme == Theme.Light ? new Bitmap(icon) : icon.Recolor(color)); + } + } + else if (key.StartsWith("road")) + { + if (entry.Value is Bitmap icon) + { + roadIcons.Add(key, systemTheme == Theme.Light ? new Bitmap(icon) : icon.Recolor(color)); + } + } + } + } + + Paint += RenderScene; + + KeyDown += HandleKeyDown; + + timer = new FormsTimer + { + Interval = 100 + }; + timer.Tick += GameTick; + + Initialize(); + + timer.Start(); + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + base.OnFormClosing(e); + timer.Stop(); + timer.Dispose(); + + foreach (var bitmap in catIcons.Values) bitmap.Dispose(); + foreach (var bitmap in roadIcons.Values) bitmap.Dispose(); + catIcons.Clear(); + roadIcons.Clear(); + } + + private void Initialize() + { + counter = JUMP_THRESHOLD; + isJumpRequested = false; + score = 0; + cat = new Cat.Running(Cat.Running.Frame.Frame0); + roads.RemoveAll(r => r == Road.Sprout); + Enumerable.Range(0, 20 - roads.Count).ToList().ForEach( + _ => roads.Add((Road)(new Random().Next(0, 3))) + ); + } + + private bool Judge() + { + if (status != GameStatus.Playing) return false; + var sproutIndices = roads + .Select((road, index) => { return road == Road.Sprout ? index : (int?)null; }) + .OfType<int>() + .ToList(); + if (cat.ViolationIndices().HasCommonElements(sproutIndices)) + { + status = GameStatus.GameOver; + return false; + } + else + { + return true; + } + } + + private void UpdateRoads() + { + var firstRoad = roads.First(); + roads.RemoveAt(0); + if (firstRoad == Road.Sprout) + { + score = Math.Min(score + 1, 999); + highScore = Math.Max(score, highScore); + } + counter = counter > 0 ? counter - 1 : limit - 1; + if (counter == 0) + { + var randomValue = new Random().Next(0, 27); + var subRoads = new List<Road>(); + if (randomValue % 3 == 0) + { + subRoads.Add(Road.Sprout); + } + if (randomValue % 9 == 0) + { + subRoads.Add(Road.Sprout); + } + if (randomValue % 27 == 0) + { + subRoads.Add(Road.Sprout); + } + roads.AddRange(subRoads); + limit = subRoads.Count == 0 ? 5 : 10; + } + if (roads.Count < 20) + { + roads.Add((Road)(new Random().Next(0, 3))); + } + } + + private void UpdateCat() + { + if (cat is Cat.Running runningCat) + { + if (runningCat.CurrentFrame == Cat.Running.Frame.Frame4 && isJumpRequested) + { + cat = new Cat.Jumping(Cat.Jumping.Frame.Frame0); + isJumpRequested = false; + return; + } + } + else if (cat is Cat.Jumping jumpingCat) + { + if (jumpingCat.CurrentFrame == Cat.Jumping.Frame.Frame9) + { + if (isJumpRequested) + { + cat = cat.Next(); + isJumpRequested = false; + return; + } + else + { + cat = new Cat.Running(Cat.Running.Frame.Frame0); + return; + } + } + } + cat = cat.Next(); + } + + private void AutoJump() + { + if (isAutoPlay && roads[JUMP_THRESHOLD - 1] == Road.Sprout) + { + isJumpRequested = true; + } + } + + private void GameTick(object? sender, EventArgs e) + { + if (Judge()) + { + UpdateRoads(); + UpdateCat(); + AutoJump(); + } + Invalidate(); + } + + private void HandleKeyDown(object? sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Space) + { + switch (status) + { + case GameStatus.NewGame: + case GameStatus.GameOver: + Initialize(); + status = GameStatus.Playing; + break; + case GameStatus.Playing when !isAutoPlay: + isJumpRequested = true; + break; + default: + break; + } + } + } + + private void RenderScene(object? sender, PaintEventArgs e) + { + var rm = Resources.ResourceManager; + var textColor = systemTheme.GetContrastColor(); + var g = e.Graphics; + + using (Font font15 = new("Consolas", 15)) + using (Brush brush = new SolidBrush(textColor)) + { + var stringFormat = new StringFormat + { + Alignment = StringAlignment.Far, + LineAlignment = StringAlignment.Center + }; + var scoreText = status == GameStatus.GameOver + ? $"HI {highScore:D3} {score:D3}" + : $"{score:D3}"; + g.DrawString(scoreText, font15, brush, new Rectangle(20, 0, 560, 50), stringFormat); + } + + roads.Take(20).Select((road, index) => new { road, index }).ToList().ForEach( + item => + { + var fileName = $"road_{item.road.GetString()}".ToLower(); + if (!roadIcons.TryGetValue(fileName, out Bitmap? image)) return; + g.DrawImage(image, new Rectangle(item.index * 30, 200, 30, 50)); + } + ); + + var fileName = $"cat_{cat.GetString()}".ToLower(); + if (!catIcons.TryGetValue(fileName, out Bitmap? image)) return; + g.DrawImage(image, new Rectangle(120, 130, 120, 100)); + + if (status != GameStatus.Playing) + { + using Brush fillBrush = new SolidBrush(Color.FromArgb(77, 0, 0, 0)); + g.FillRectangle(fillBrush, new Rectangle(0, 0, 600, 250)); + + using Font font18 = new("Segoe UI", 16, FontStyle.Bold); + using Brush brush = new SolidBrush(textColor); + var message = Strings.Game_PressSpaceToPlay; + if (status == GameStatus.GameOver) + { + if (score >= highScore) + { + SaveRecord(score); + message = $"{Strings.Game_NewRecord}!!\n{message}"; + } + else + { + message = $"{Strings.Game_GameOver}\n{message}"; + } + } + var stringFormat = new StringFormat + { + Alignment = StringAlignment.Center, + LineAlignment = StringAlignment.Center + }; + g.DrawString(message, font18, brush, new Rectangle(0, 0, 600, 250), stringFormat); + } + } + private void SaveRecord(int score) + { + UserSettings.Default.HighScore = score; + UserSettings.Default.Save(); + } + } + + internal static class ListExtension + { + internal static bool HasCommonElements(this List<int> list1, List<int> list2) + { + return list1.Intersect(list2).Any(); + } + } +} \ No newline at end of file diff --git a/RunCat365/EndlessGameForm.resx b/RunCat365/EndlessGameForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/RunCat365/EndlessGameForm.resx @@ -0,0 +1,120 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> +</root> \ No newline at end of file diff --git a/RunCat365/FPSMaxLimit.cs b/RunCat365/FPSMaxLimit.cs new file mode 100644 index 0000000..d30dc3e --- /dev/null +++ b/RunCat365/FPSMaxLimit.cs @@ -0,0 +1,76 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Diagnostics.CodeAnalysis; + +namespace RunCat365 +{ + enum FPSMaxLimit + { + FPS40, + FPS30, + FPS20, + FPS10, + } + + internal static class FPSMaxLimitExtension + { + internal static string GetString(this FPSMaxLimit fpsMaxLimit) + { + return fpsMaxLimit switch + { + FPSMaxLimit.FPS40 => "40fps", + FPSMaxLimit.FPS30 => "30fps", + FPSMaxLimit.FPS20 => "20fps", + FPSMaxLimit.FPS10 => "10fps", + _ => "", + }; + } + + internal static float GetRate(this FPSMaxLimit fpsMaxLimit) + { + return fpsMaxLimit switch + { + FPSMaxLimit.FPS40 => 1f, + FPSMaxLimit.FPS30 => 0.75f, + FPSMaxLimit.FPS20 => 0.5f, + FPSMaxLimit.FPS10 => 0.25f, + _ => 1f, + }; + } + + internal static bool TryParse([NotNullWhen(true)] string? value, out FPSMaxLimit result) + { + FPSMaxLimit? nullableResult = value switch + { + "40fps" => FPSMaxLimit.FPS40, + "30fps" => FPSMaxLimit.FPS30, + "20fps" => FPSMaxLimit.FPS20, + "10fps" => FPSMaxLimit.FPS10, + _ => null, + }; + + if (nullableResult is FPSMaxLimit nonNullableResult) + { + result = nonNullableResult; + return true; + } + else + { + result = FPSMaxLimit.FPS40; + return false; + } + } + } +} diff --git a/RunCat365/FlatTrackBar.cs b/RunCat365/FlatTrackBar.cs new file mode 100644 index 0000000..364180a --- /dev/null +++ b/RunCat365/FlatTrackBar.cs @@ -0,0 +1,179 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.ComponentModel; +using System.Drawing.Drawing2D; + +namespace RunCat365 +{ + internal sealed class FlatTrackBar : Control + { + private int minimum = 1; + private int maximum = 10; + private int currentValue = 5; + private bool dragging; + + internal event EventHandler? ValueChanged; + + [DefaultValue(1)] + internal int Minimum + { + get => minimum; + set + { + minimum = value; + if (currentValue < minimum) Value = minimum; + Invalidate(); + } + } + + [DefaultValue(10)] + internal int Maximum + { + get => maximum; + set + { + maximum = value; + if (currentValue > maximum) Value = maximum; + Invalidate(); + } + } + + [DefaultValue(5)] + internal int Value + { + get => currentValue; + set + { + var clamped = Math.Clamp(value, minimum, maximum); + if (currentValue == clamped) return; + currentValue = clamped; + Invalidate(); + ValueChanged?.Invoke(this, EventArgs.Empty); + } + } + + internal Color TrackColor { get; set; } = Color.FromArgb(110, 110, 110); + internal Color TrackFilledColor { get; set; } = Color.FromArgb(180, 180, 180); + internal Color ThumbColor { get; set; } = Color.FromArgb(70, 120, 200); + internal Color ThumbBorderColor { get; set; } = Color.FromArgb(140, 170, 220); + + internal int TrackThickness { get; set; } = 3; + internal int ThumbWidth { get; set; } = 12; + internal int ThumbHeight { get; set; } = 18; + + public FlatTrackBar() + { + SetStyle( + ControlStyles.AllPaintingInWmPaint | + ControlStyles.OptimizedDoubleBuffer | + ControlStyles.UserPaint | + ControlStyles.SupportsTransparentBackColor | + ControlStyles.ResizeRedraw, + true); + BackColor = Color.Transparent; + Height = 24; + Cursor = Cursors.Hand; + } + + private Rectangle GetTrackRectangle() + { + var halfThumb = ThumbWidth / 2; + var trackY = (Height - TrackThickness) / 2; + return new Rectangle(halfThumb, trackY, Math.Max(0, Width - ThumbWidth), TrackThickness); + } + + private int GetThumbCenterX() + { + var range = Math.Max(1, maximum - minimum); + var ratio = (float)(currentValue - minimum) / range; + var trackWidth = Math.Max(0, Width - ThumbWidth); + return ThumbWidth / 2 + (int)(trackWidth * ratio); + } + + protected override void OnPaint(PaintEventArgs e) + { + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + + var track = GetTrackRectangle(); + var thumbCenterX = GetThumbCenterX(); + + using var trackBrush = new SolidBrush(TrackColor); + e.Graphics.FillRectangle(trackBrush, track); + + var filledRect = new Rectangle(track.X, track.Y, thumbCenterX - track.X, track.Height); + if (filledRect.Width > 0) + { + using var filledBrush = new SolidBrush(TrackFilledColor); + e.Graphics.FillRectangle(filledBrush, filledRect); + } + + var thumbRect = new Rectangle( + thumbCenterX - ThumbWidth / 2, + (Height - ThumbHeight) / 2, + ThumbWidth, + ThumbHeight); + using var thumbBrush = new SolidBrush(ThumbColor); + using var thumbPen = new Pen(ThumbBorderColor); + e.Graphics.FillRectangle(thumbBrush, thumbRect); + e.Graphics.DrawRectangle(thumbPen, thumbRect.X, thumbRect.Y, thumbRect.Width - 1, thumbRect.Height - 1); + } + + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + if (e.Button != MouseButtons.Left) return; + dragging = true; + UpdateValueFromMouseX(e.X); + Focus(); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + if (!dragging) return; + UpdateValueFromMouseX(e.X); + } + + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + dragging = false; + } + + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + if (e.KeyCode == Keys.Left || e.KeyCode == Keys.Down) + { + Value = currentValue - 1; + e.Handled = true; + } + else if (e.KeyCode == Keys.Right || e.KeyCode == Keys.Up) + { + Value = currentValue + 1; + e.Handled = true; + } + } + + private void UpdateValueFromMouseX(int x) + { + var trackWidth = Math.Max(1, Width - ThumbWidth); + var relativeX = Math.Clamp(x - ThumbWidth / 2, 0, trackWidth); + var ratio = (float)relativeX / trackWidth; + var newValue = minimum + (int)Math.Round(ratio * (maximum - minimum)); + Value = newValue; + } + } +} diff --git a/RunCat365/FrameAnimationView.cs b/RunCat365/FrameAnimationView.cs new file mode 100644 index 0000000..169a51e --- /dev/null +++ b/RunCat365/FrameAnimationView.cs @@ -0,0 +1,123 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.ComponentModel; +using System.Drawing.Drawing2D; + +namespace RunCat365 +{ + internal sealed class FrameAnimationView : Control + { + private readonly System.Threading.Timer timer; + private IReadOnlyList<Bitmap> frames = []; + private int currentFrame = 0; + private int intervalMs = 100; + private volatile bool running = false; + + internal FrameAnimationView() + { + SetStyle( + ControlStyles.AllPaintingInWmPaint | + ControlStyles.OptimizedDoubleBuffer | + ControlStyles.UserPaint | + ControlStyles.ResizeRedraw, + true); + timer = new System.Threading.Timer(OnTimerTick, null, Timeout.Infinite, Timeout.Infinite); + } + + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal int FrameIntervalMs + { + get => intervalMs; + set => intervalMs = Math.Max(1, value); + } + + internal void SetFrames(IReadOnlyList<Bitmap> newFrames) + { + frames = newFrames; + currentFrame = 0; + Invalidate(); + if (frames.Count > 1) + { + running = true; + timer.Change(intervalMs, Timeout.Infinite); + } + else + { + running = false; + timer.Change(Timeout.Infinite, Timeout.Infinite); + } + } + + internal void Clear() + { + running = false; + timer.Change(Timeout.Infinite, Timeout.Infinite); + frames = []; + currentFrame = 0; + Invalidate(); + } + + private void OnTimerTick(object? state) + { + if (!running) return; + if (IsDisposed || !IsHandleCreated) return; + try + { + BeginInvoke(AdvanceFrame); + } + catch (Exception ex) when (ex is InvalidOperationException or ObjectDisposedException) + { + return; + } + if (running) timer.Change(intervalMs, Timeout.Infinite); + } + + private void AdvanceFrame() + { + if (!running || frames.Count < 2) return; + currentFrame = (currentFrame + 1) % frames.Count; + Invalidate(); + } + + protected override void OnPaint(PaintEventArgs e) + { + e.Graphics.Clear(BackColor); + if (frames.Count == 0 || currentFrame >= frames.Count) return; + + var frame = frames[currentFrame]; + e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + e.Graphics.PixelOffsetMode = PixelOffsetMode.Half; + + var scale = Math.Min( + (float)ClientSize.Width / frame.Width, + (float)ClientSize.Height / frame.Height); + var width = (int)(frame.Width * scale); + var height = (int)(frame.Height * scale); + var x = (ClientSize.Width - width) / 2; + var y = (ClientSize.Height - height) / 2; + e.Graphics.DrawImage(frame, x, y, width, height); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + running = false; + timer.Dispose(); + } + base.Dispose(disposing); + } + } +} diff --git a/RunCat365/GPURepository.cs b/RunCat365/GPURepository.cs new file mode 100644 index 0000000..b06f2aa --- /dev/null +++ b/RunCat365/GPURepository.cs @@ -0,0 +1,122 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; + +namespace RunCat365 +{ + struct GPUInfo + { + internal float Average { get; set; } + internal float Maximum { get; set; } + } + + internal static class GPUInfoExtension + { + internal static string GetDescription(this GPUInfo gpuInfo) + { + return $"{Strings.SystemInfo_GPU}: {gpuInfo.Maximum:f1}%"; + } + + internal static List<string> GenerateIndicator(this GPUInfo gpuInfo) + { + var resultLines = new List<string> + { + TreeFormatter.CreateRoot($"{Strings.SystemInfo_GPU}:"), + TreeFormatter.CreateNode($"{Strings.SystemInfo_Average}: {gpuInfo.Average:f1}%", false), + TreeFormatter.CreateNode($"{Strings.SystemInfo_Maximum}: {gpuInfo.Maximum:f1}%", true) + }; + return resultLines; + } + } + + internal sealed class GPUPerformanceCounters : InstancedPerformanceCounters + { + private const string ENGINE_TYPE_FILTER = "engtype_3D"; + + protected override string CategoryName => "GPU Engine"; + protected override string CounterName => "Utilization Percentage"; + + protected override bool ShouldIncludeInstance(string instanceName) + { + return instanceName.Contains(ENGINE_TYPE_FILTER, StringComparison.Ordinal); + } + + internal static GPUPerformanceCounters? TryCreate() + { + var instance = new GPUPerformanceCounters(); + return instance.TryInitialize() ? instance : null; + } + } + + internal class GPURepository + { + private const int GPU_INFO_LIST_LIMIT_SIZE = 5; + private const int REFRESH_INTERVAL_TICKS = 30; + + private readonly GPUPerformanceCounters? counters; + private readonly List<GPUInfo> gpuInfoList = []; + private int ticksSinceLastRefresh; + + internal bool IsAvailable => counters is not null; + + internal GPURepository() + { + counters = GPUPerformanceCounters.TryCreate(); + } + + internal void Update() + { + if (counters is null) return; + + ticksSinceLastRefresh += 1; + if (REFRESH_INTERVAL_TICKS <= ticksSinceLastRefresh) + { + ticksSinceLastRefresh = 0; + counters.RefreshInstances(); + } + + var values = counters.ReadValues(); + if (values.Count == 0) return; + + var gpuInfo = new GPUInfo + { + Average = Math.Min(100, values.Average()), + Maximum = Math.Min(100, values.Max()) + }; + + gpuInfoList.Add(gpuInfo); + if (GPU_INFO_LIST_LIMIT_SIZE < gpuInfoList.Count) + { + gpuInfoList.RemoveAt(0); + } + } + + internal GPUInfo? Get() + { + if (counters is null || gpuInfoList.Count == 0) return null; + + return new GPUInfo + { + Average = gpuInfoList.Average(x => x.Average), + Maximum = gpuInfoList.Max(x => x.Maximum) + }; + } + + internal void Close() + { + counters?.Close(); + } + } +} diff --git a/RunCat365/GameStatus.cs b/RunCat365/GameStatus.cs new file mode 100644 index 0000000..22e489d --- /dev/null +++ b/RunCat365/GameStatus.cs @@ -0,0 +1,23 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace RunCat365 +{ + internal enum GameStatus + { + NewGame, + Playing, + GameOver + } +} diff --git a/RunCat365/InstancedPerformanceCounters.cs b/RunCat365/InstancedPerformanceCounters.cs new file mode 100644 index 0000000..3b72c5e --- /dev/null +++ b/RunCat365/InstancedPerformanceCounters.cs @@ -0,0 +1,130 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Diagnostics; + +namespace RunCat365 +{ + internal abstract class InstancedPerformanceCounters + { + private readonly Dictionary<string, PerformanceCounter> countersByInstance = []; + + protected abstract string CategoryName { get; } + protected abstract string CounterName { get; } + + protected virtual bool ShouldIncludeInstance(string instanceName) => true; + + internal int Count => countersByInstance.Count; + + protected bool TryInitialize() + { + try + { + _ = new PerformanceCounterCategory(CategoryName); + } + catch + { + return false; + } + + RefreshInstances(); + return Count != 0; + } + + internal void RefreshInstances() + { + string[] currentInstanceNames; + try + { + var category = new PerformanceCounterCategory(CategoryName); + currentInstanceNames = category.GetInstanceNames() + .Where(ShouldIncludeInstance) + .ToArray(); + } + catch (Exception exception) when (IsExpectedCounterException(exception)) + { + Debug.WriteLine($"{GetType().Name}.RefreshInstances failed: {exception.Message}"); + return; + } + + var currentSet = new HashSet<string>(currentInstanceNames, StringComparer.Ordinal); + var staleInstances = countersByInstance.Keys + .Where(name => !currentSet.Contains(name)) + .ToList(); + foreach (var instanceName in staleInstances) + { + countersByInstance[instanceName].Close(); + countersByInstance.Remove(instanceName); + } + + foreach (var instanceName in currentInstanceNames) + { + if (countersByInstance.ContainsKey(instanceName)) continue; + PerformanceCounter? counter = null; + try + { + counter = new PerformanceCounter(CategoryName, CounterName, instanceName); + _ = counter.NextValue(); + countersByInstance[instanceName] = counter; + counter = null; + } + catch (Exception exception) when (IsExpectedCounterException(exception)) + { + Debug.WriteLine($"{GetType().Name}: failed to create counter for {instanceName}: {exception.Message}"); + } + finally + { + counter?.Close(); + } + } + } + + internal List<float> ReadValues() + { + var values = new List<float>(countersByInstance.Count); + var deadInstances = new List<string>(); + foreach (var pair in countersByInstance) + { + try + { + values.Add(pair.Value.NextValue()); + } + catch (Exception exception) when (IsExpectedCounterException(exception)) + { + Debug.WriteLine($"{GetType().Name}: counter {pair.Key} failed: {exception.Message}"); + deadInstances.Add(pair.Key); + } + } + foreach (var instanceName in deadInstances) + { + countersByInstance[instanceName].Close(); + countersByInstance.Remove(instanceName); + } + return values; + } + + internal void Close() + { + foreach (var counter in countersByInstance.Values) counter.Close(); + countersByInstance.Clear(); + } + + private static bool IsExpectedCounterException(Exception exception) + { + return exception is InvalidOperationException + or System.ComponentModel.Win32Exception + or UnauthorizedAccessException; + } + } +} diff --git a/RunCat365/LaunchAtStartupManager.cs b/RunCat365/LaunchAtStartupManager.cs new file mode 100644 index 0000000..a11fbfb --- /dev/null +++ b/RunCat365/LaunchAtStartupManager.cs @@ -0,0 +1,137 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Win32; +using Windows.ApplicationModel; + +namespace RunCat365 +{ + internal interface ILaunchAtStartupManager + { + bool GetEnabled(); + bool Toggle(bool currentlyEnabled); + } + + internal sealed class PackagedLaunchAtStartupManager : ILaunchAtStartupManager + { + private static StartupTask? startupTask; + + public bool GetEnabled() + { + startupTask ??= Task.Run(async () => await StartupTask.GetAsync("RunCatStartup")).Result; + if (startupTask is null) return false; + if (startupTask.State == StartupTaskState.Enabled) return true; + return false; + } + + public bool Toggle(bool currentlyEnabled) + { + startupTask ??= Task.Run(async () => await StartupTask.GetAsync("RunCatStartup")).Result; + if (currentlyEnabled) + { + if (startupTask.State == StartupTaskState.Enabled) startupTask.Disable(); + return true; + } + else + { + switch (startupTask.State) + { + case StartupTaskState.Enabled: + return true; + case StartupTaskState.Disabled: + var newStartupState = Task.Run(async () => await startupTask.RequestEnableAsync()).Result; + if (newStartupState == StartupTaskState.Enabled) + { + return true; + } + else + { + throw new InvalidOperationException("Launch at Startup could not be activated."); + } + case StartupTaskState.DisabledByUser: + throw new InvalidOperationException("Launch at startup was disabled by the user, enable it in Task Manager > Startup, search RunCat 365 and enable it."); + case StartupTaskState.DisabledByPolicy: + throw new InvalidOperationException("Launch at startup was disabled by policy."); + default: + return false; + } + } + } + } + + internal sealed class UnpackagedLaunchAtStartupManager : ILaunchAtStartupManager + { + public bool GetEnabled() + { + var keyName = @"Software\Microsoft\Windows\CurrentVersion\Run"; + using var rKey = Registry.CurrentUser.OpenSubKey(keyName); + if (rKey is null) return false; + var value = (rKey.GetValue(Application.ProductName) is not null); + rKey.Close(); + return value; + } + + public bool Toggle(bool currentlyEnabled) + { + var productName = Application.ProductName; + if (productName is null) return false; + var keyName = @"Software\Microsoft\Windows\CurrentVersion\Run"; + using var rKey = Registry.CurrentUser.OpenSubKey(keyName, true); + if (rKey is null) return false; + if (currentlyEnabled) + { + rKey.DeleteValue(productName, false); + } + else + { + var fileName = Environment.ProcessPath; + if (fileName is not null) + { + rKey.SetValue(productName, fileName); + } + } + rKey.Close(); + return true; + } + } + + internal class LaunchAtStartupManager + { + private readonly ILaunchAtStartupManager launchAtStartupManager; + + public LaunchAtStartupManager() + { + launchAtStartupManager = IsRunningAsPackaged() + ? new PackagedLaunchAtStartupManager() + : new UnpackagedLaunchAtStartupManager(); + } + + public bool GetStartup() => launchAtStartupManager.GetEnabled(); + + public bool ToggleStartup(bool currentlyEnabled) => launchAtStartupManager.Toggle(currentlyEnabled); + + private static bool IsRunningAsPackaged() + { + try + { + _ = Package.Current; + return true; + } + catch (InvalidOperationException) + { + return false; + } + } + } +} diff --git a/RunCat365/MemoryRepository.cs b/RunCat365/MemoryRepository.cs new file mode 100644 index 0000000..060bbae --- /dev/null +++ b/RunCat365/MemoryRepository.cs @@ -0,0 +1,94 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; +using System.Runtime.InteropServices; + +namespace RunCat365 +{ + struct MemoryInfo + { + internal uint MemoryLoad { get; set; } + internal long TotalMemory { get; set; } + internal long AvailableMemory { get; set; } + internal long UsedMemory { get; set; } + } + + internal static class MemoryInfoExtension + { + internal static string GetDescription(this MemoryInfo memoryInfo) + { + return $"{Strings.SystemInfo_Memory}: {memoryInfo.MemoryLoad}%"; + } + + internal static List<string> GenerateIndicator(this MemoryInfo memoryInfo) + { + var resultLines = new List<string> + { + TreeFormatter.CreateRoot($"{Strings.SystemInfo_Memory}: {memoryInfo.MemoryLoad}%"), + TreeFormatter.CreateNode($"{Strings.SystemInfo_Total}: {memoryInfo.TotalMemory.ToByteFormatted()}", false), + TreeFormatter.CreateNode($"{Strings.SystemInfo_Used}: {memoryInfo.UsedMemory.ToByteFormatted()}", false), + TreeFormatter.CreateNode($"{Strings.SystemInfo_Available}: {memoryInfo.AvailableMemory.ToByteFormatted()}", true) + }; + return resultLines; + } + } + + internal partial class MemoryRepository + { + private MemoryInfo memoryInfo; + + internal MemoryRepository() + { + memoryInfo = new MemoryInfo(); + } + + internal void Update() + { + var memStatus = new MemoryStatusEx(); + memStatus.dwLength = (uint)Marshal.SizeOf(memStatus); + + if (GlobalMemoryStatusEx(ref memStatus)) + { + memoryInfo.MemoryLoad = memStatus.dwMemoryLoad; + memoryInfo.TotalMemory = (long)memStatus.ullTotalPhys; + memoryInfo.AvailableMemory = (long)memStatus.ullAvailPhys; + memoryInfo.UsedMemory = (long)(memStatus.ullTotalPhys - memStatus.ullAvailPhys); + } + } + + internal MemoryInfo Get() + { + return memoryInfo; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MemoryStatusEx + { + public uint dwLength; + public uint dwMemoryLoad; + public ulong ullTotalPhys; + public ulong ullAvailPhys; + public ulong ullTotalPageFile; + public ulong ullAvailPageFile; + public ulong ullTotalVirtual; + public ulong ullAvailVirtual; + public ulong ullAvailExtendedVirtual; + } + + [LibraryImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static partial bool GlobalMemoryStatusEx(ref MemoryStatusEx lpBuffer); + } +} diff --git a/RunCat365/NetworkRepository.cs b/RunCat365/NetworkRepository.cs new file mode 100644 index 0000000..59833cc --- /dev/null +++ b/RunCat365/NetworkRepository.cs @@ -0,0 +1,122 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; +using System.Net.NetworkInformation; + +namespace RunCat365 +{ + struct NetworkInfo + { + internal float SentSpeed { get; set; } + internal float ReceivedSpeed { get; set; } + } + + internal static class NetworkInfoExtension + { + internal static List<string> GenerateIndicator(this NetworkInfo networkInfo) + { + return [ + TreeFormatter.CreateRoot($"{Strings.SystemInfo_Network}:"), + TreeFormatter.CreateNode($"{Strings.SystemInfo_Sent}: {FormatSpeed(networkInfo.SentSpeed)}", false), + TreeFormatter.CreateNode($"{Strings.SystemInfo_Received}: {FormatSpeed(networkInfo.ReceivedSpeed)}", true) + ]; + } + + private static string FormatSpeed(float speedBytes) + { + return ((long)speedBytes).ToByteFormatted() + "/s"; + } + } + + internal class NetworkRepository + { + private readonly NetworkInterface? networkInterface; + private long lastSent; + private long lastReceived; + private DateTime lastUpdate; + private NetworkInfo? networkInfo; + + internal bool IsAvailable => networkInterface is not null; + + internal NetworkRepository() + { + networkInterface = GetActiveNetworkInterface(); + if (networkInterface is null) return; + var stats = networkInterface.GetIPStatistics(); + lastSent = stats.BytesSent; + lastReceived = stats.BytesReceived; + lastUpdate = DateTime.UtcNow; + } + + private static NetworkInterface? GetActiveNetworkInterface() + { + try + { + var interfaces = NetworkInterface.GetAllNetworkInterfaces(); + return interfaces.FirstOrDefault(IsValidNetworkInterface); + } + catch (NetworkInformationException) + { + return null; + } + } + + private static bool IsValidNetworkInterface(NetworkInterface networkInterface) + { + if (networkInterface.NetworkInterfaceType == NetworkInterfaceType.Loopback) return false; + if (networkInterface.NetworkInterfaceType == NetworkInterfaceType.Tunnel) return false; + if (networkInterface.OperationalStatus != OperationalStatus.Up) return false; + + var description = networkInterface.Description.ToLower(); + if (description.Contains("vpn")) return false; + if (description.Contains("tap")) return false; + if (description.Contains("virtual")) return false; + if (description.Contains("tun")) return false; + + return true; + } + + internal void Update() + { + if (networkInterface is null) return; + try + { + var stats = networkInterface.GetIPStatistics(); + var now = DateTime.UtcNow; + var elapsedSec = (now - lastUpdate).TotalSeconds; + if (elapsedSec > 0) + { + networkInfo = new NetworkInfo + { + SentSpeed = (float)((stats.BytesSent - lastSent) / elapsedSec), + ReceivedSpeed = (float)((stats.BytesReceived - lastReceived) / elapsedSec) + }; + } + lastSent = stats.BytesSent; + lastReceived = stats.BytesReceived; + lastUpdate = now; + } + catch (NetworkInformationException) + { + networkInfo = null; + } + } + + internal NetworkInfo? Get() + { + return networkInfo; + } + } +} diff --git a/RunCat365/Program.cs b/RunCat365/Program.cs new file mode 100644 index 0000000..85931c4 --- /dev/null +++ b/RunCat365/Program.cs @@ -0,0 +1,404 @@ +// Copyright 2020 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Microsoft.Win32; +using RunCat365.Properties; +using System.Diagnostics; +using System.Globalization; +using FormsTimer = System.Windows.Forms.Timer; + +namespace RunCat365 +{ + internal static class Program + { + [STAThread] + static void Main() + { +#if DEBUG + var defaultCultureInfo = SupportedLanguage.English.GetDefaultCultureInfo(); +#else + var defaultCultureInfo = SupportedLanguageExtension.GetCurrentLanguage().GetDefaultCultureInfo(); +#endif + CultureInfo.CurrentUICulture = defaultCultureInfo; + CultureInfo.CurrentCulture = defaultCultureInfo; + + using var procMutex = new Mutex(true, "_RUNCAT_MUTEX", out var result); + if (!result) return; + + try + { + ApplicationConfiguration.Initialize(); + Application.SetColorMode(SystemColorMode.System); + Application.Run(new RunCat365ApplicationContext()); + } + finally + { + procMutex?.ReleaseMutex(); + } + } + } + + internal class RunCat365ApplicationContext : ApplicationContext + { + private const int FETCH_TIMER_DEFAULT_INTERVAL = 1000; + private const int FETCH_COUNTER_SIZE = 5; + private const int ANIMATE_TIMER_DEFAULT_INTERVAL = 200; + private readonly CPURepository cpuRepository; + private readonly GPURepository gpuRepository; + private readonly MemoryRepository memoryRepository; + private readonly TemperatureRepository temperatureRepository; + private readonly StorageRepository storageRepository; + private readonly NetworkRepository networkRepository; + private readonly CustomRunnerRepository customRunnerRepository; + private readonly LaunchAtStartupManager launchAtStartupManager; + private readonly ContextMenuManager contextMenuManager; + private readonly FormsTimer fetchTimer; + private readonly FormsTimer animateTimer; + private Runner runner = Runner.Cat; + private Theme manualTheme = Theme.System; + private TemperatureUnit temperatureUnit = TemperatureUnit.System; + private FPSMaxLimit fpsMaxLimit = FPSMaxLimit.FPS40; + private SpeedSource speedSource = SpeedSource.CPU; + private string? customRunnerName; + private int fetchCounter = 5; + private bool isFetching; + + public RunCat365ApplicationContext() + { + UserSettings.Default.Reload(); + _ = Enum.TryParse(UserSettings.Default.Runner, out runner); + _ = Enum.TryParse(UserSettings.Default.Theme, out manualTheme); + _ = Enum.TryParse(UserSettings.Default.TemperatureUnit, out temperatureUnit); + _ = Enum.TryParse(UserSettings.Default.FPSMaxLimit, out fpsMaxLimit); + _ = Enum.TryParse(UserSettings.Default.SpeedSource, out speedSource); + customRunnerName = string.IsNullOrEmpty(UserSettings.Default.CustomRunnerName) + ? null + : UserSettings.Default.CustomRunnerName; + + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(UserPreferenceChanged); + + cpuRepository = new CPURepository(); + gpuRepository = new GPURepository(); + memoryRepository = new MemoryRepository(); + temperatureRepository = new TemperatureRepository(); + storageRepository = new StorageRepository(); + networkRepository = new NetworkRepository(); + customRunnerRepository = new CustomRunnerRepository(); + launchAtStartupManager = new LaunchAtStartupManager(); + + ResolveSpeedSource(); + + contextMenuManager = new ContextMenuManager( + () => runner, + r => ChangeRunner(r), + customRunnerRepository, + () => customRunnerName, + name => ApplyCustomRunner(name), + deletedName => HandleCustomRunnerDeleted(deletedName), + () => GetSystemTheme(), + () => manualTheme, + t => ChangeManualTheme(t), + () => speedSource, + s => ChangeSpeedSource(s), + s => IsSpeedSourceAvailable(s), + () => fpsMaxLimit, + f => ChangeFPSMaxLimit(f), + () => temperatureUnit, + u => ChangeTemperatureUnit(u), + () => launchAtStartupManager.GetStartup(), + s => launchAtStartupManager.ToggleStartup(s), + () => OpenProjectPage(), + () => Application.Exit() + ); + + if (customRunnerName is not null) + { + ApplyCustomRunner(customRunnerName); + } + + animateTimer = new FormsTimer + { + Interval = ANIMATE_TIMER_DEFAULT_INTERVAL + }; + animateTimer.Tick += new EventHandler(AnimationTick); + animateTimer.Start(); + + fetchTimer = new FormsTimer + { + Interval = FETCH_TIMER_DEFAULT_INTERVAL + }; + fetchTimer.Tick += new EventHandler(FetchTick); + fetchTimer.Start(); + + ShowBalloonTipIfNeeded(); + } + + private static Theme GetSystemTheme() + { + var keyName = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"; + using var rKey = Registry.CurrentUser.OpenSubKey(keyName); + if (rKey is null) return Theme.Light; + var value = rKey.GetValue("SystemUsesLightTheme"); + if (value is null) return Theme.Light; + return (int)value == 0 ? Theme.Dark : Theme.Light; + } + + private bool IsSpeedSourceAvailable(SpeedSource speedSource) + { + return speedSource switch + { + SpeedSource.CPU => true, + SpeedSource.GPU => gpuRepository.IsAvailable, + SpeedSource.Memory => true, + _ => false, + }; + } + + private void ResolveSpeedSource() + { + if (!IsSpeedSourceAvailable(speedSource)) + { + ChangeSpeedSource(SpeedSource.CPU); + } + } + + private void ShowBalloonTipIfNeeded() + { + if (!cpuRepository.IsAvailable) + { + contextMenuManager.ShowBalloonTip(BalloonTipType.CPUInfoUnavailable); + } + else if (UserSettings.Default.FirstLaunch) + { + contextMenuManager.ShowBalloonTip(BalloonTipType.AppLaunched); + UserSettings.Default.FirstLaunch = false; + UserSettings.Default.Save(); + } + } + + private void UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + if (e.Category != UserPreferenceCategory.General) return; + var systemTheme = GetSystemTheme(); + if (contextMenuManager.HasActiveCustomIcons) + { + contextMenuManager.RecolorActiveCustomIcons(systemTheme, manualTheme); + } + else + { + contextMenuManager.SetIcons(systemTheme, manualTheme, runner); + } + } + + private static void OpenProjectPage() + { + try + { + Process.Start(new ProcessStartInfo() + { + FileName = "https://github.com/runcat-dev/RunCat365", + UseShellExecute = true + }); + } + catch (Exception e) + { + Console.WriteLine($"Error: {e.Message}"); + } + } + + private void ChangeRunner(Runner r) + { + runner = r; + customRunnerName = null; + UserSettings.Default.Runner = runner.ToString(); + UserSettings.Default.CustomRunnerName = string.Empty; + UserSettings.Default.Save(); + } + + private void ApplyCustomRunner(string name) + { + var frames = customRunnerRepository.LoadFrames(name); + if (frames.Count == 0) return; + customRunnerName = name; + UserSettings.Default.CustomRunnerName = name; + UserSettings.Default.Save(); + contextMenuManager.SetCustomIcons(frames, GetSystemTheme(), manualTheme); + foreach (var frame in frames) frame.Dispose(); + } + + private void RevertToBuiltInRunner() + { + customRunnerName = null; + UserSettings.Default.CustomRunnerName = string.Empty; + UserSettings.Default.Save(); + contextMenuManager.SetIcons(GetSystemTheme(), manualTheme, runner); + } + + private void HandleCustomRunnerDeleted(string deletedName) + { + if (!string.Equals(customRunnerName, deletedName, StringComparison.OrdinalIgnoreCase)) return; + RevertToBuiltInRunner(); + } + + private void ChangeManualTheme(Theme t) + { + manualTheme = t; + UserSettings.Default.Theme = manualTheme.ToString(); + UserSettings.Default.Save(); + } + + private void ChangeTemperatureUnit(TemperatureUnit u) + { + temperatureUnit = u; + UserSettings.Default.TemperatureUnit = temperatureUnit.ToString(); + UserSettings.Default.Save(); + } + + private void ChangeSpeedSource(SpeedSource s) + { + speedSource = s; + UserSettings.Default.SpeedSource = speedSource.ToString(); + UserSettings.Default.Save(); + } + + private void ChangeFPSMaxLimit(FPSMaxLimit f) + { + fpsMaxLimit = f; + UserSettings.Default.FPSMaxLimit = fpsMaxLimit.ToString(); + UserSettings.Default.Save(); + } + + private void AnimationTick(object? sender, EventArgs e) + { + contextMenuManager.AdvanceFrame(); + } + + private string GetInfoDescription(CPUInfo cpuInfo, GPUInfo? gpuInfo, MemoryInfo memoryInfo, TemperatureInfo? temperatureInfo) + { + var baseDescription = speedSource switch + { + SpeedSource.CPU => cpuInfo.GetDescription(), + SpeedSource.GPU => gpuInfo?.GetDescription() ?? "", + SpeedSource.Memory => memoryInfo.GetDescription(), + _ => "", + }; + + var temperatureDescription = temperatureInfo?.GetDescription(temperatureUnit) ?? ""; + return string.IsNullOrEmpty(temperatureDescription) ? baseDescription : $"{baseDescription}\n{temperatureDescription}"; + } + + private int CalculateInterval(CPUInfo cpuInfo, GPUInfo? gpuInfo, MemoryInfo memoryInfo) + { + var load = speedSource switch + { + SpeedSource.CPU => cpuInfo.Total, + SpeedSource.GPU => gpuInfo?.Maximum ?? 0f, + SpeedSource.Memory => memoryInfo.MemoryLoad, + _ => 0f, + }; + var speed = (float)Math.Max(1.0f, (load / 5.0f) * fpsMaxLimit.GetRate()); + return (int)(500.0f / speed); + } + + private int FetchSystemInfo() + { + var cpuInfo = cpuRepository.Get(); + var gpuInfo = gpuRepository.Get(); + var memoryInfo = memoryRepository.Get(); + var temperatureInfo = temperatureRepository.Get(); + var storageInfo = storageRepository.Get(); + var networkInfo = networkRepository.Get(); + + contextMenuManager.SetNotifyIconText(GetInfoDescription(cpuInfo, gpuInfo, memoryInfo, temperatureInfo)); + + var systemInfoValues = new List<string>(); + systemInfoValues.AddRange(cpuInfo.GenerateIndicator()); + if (gpuInfo.HasValue) + { + systemInfoValues.AddRange(gpuInfo.Value.GenerateIndicator()); + } + systemInfoValues.AddRange(memoryInfo.GenerateIndicator()); + if (temperatureInfo.HasValue) + { + systemInfoValues.AddRange(temperatureInfo.Value.GenerateIndicator(temperatureUnit)); + } + systemInfoValues.AddRange(storageInfo.GenerateIndicator()); + if (networkInfo.HasValue) + { + systemInfoValues.AddRange(networkInfo.Value.GenerateIndicator()); + } + contextMenuManager.SetSystemInfoMenuText(string.Join("\n", [.. systemInfoValues])); + + return CalculateInterval(cpuInfo, gpuInfo, memoryInfo); + } + + private async void FetchTick(object? sender, EventArgs e) + { + if (isFetching) return; + isFetching = true; + try + { + var doHeavySlice = await Task.Run(() => + { + cpuRepository.Update(); + gpuRepository.Update(); + fetchCounter += 1; + if (fetchCounter < FETCH_COUNTER_SIZE) return false; + fetchCounter = 0; + temperatureRepository.Update(); + memoryRepository.Update(); + storageRepository.Update(); + networkRepository.Update(); + return true; + }); + + if (!doHeavySlice) return; + + var interval = FetchSystemInfo(); + animateTimer.Stop(); + animateTimer.Interval = interval; + animateTimer.Start(); + } + catch (Exception exception) + { + Console.WriteLine($"FetchTick failed: {exception.Message}"); + } + finally + { + isFetching = false; + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + SystemEvents.UserPreferenceChanged -= UserPreferenceChanged; + + animateTimer?.Stop(); + animateTimer?.Dispose(); + fetchTimer?.Stop(); + fetchTimer?.Dispose(); + + cpuRepository?.Close(); + gpuRepository?.Close(); + temperatureRepository?.Close(); + + contextMenuManager?.HideNotifyIcon(); + contextMenuManager?.Dispose(); + } + base.Dispose(disposing); + } + } +} diff --git a/RunCat365/Properties/Resources.Designer.cs b/RunCat365/Properties/Resources.Designer.cs new file mode 100644 index 0000000..452fc83 --- /dev/null +++ b/RunCat365/Properties/Resources.Designer.cs @@ -0,0 +1,483 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace RunCat365.Properties { + using System; + + + /// <summary> + /// A strongly-typed resource class, for looking up localized strings, etc. + /// </summary> + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// <summary> + /// Returns the cached ResourceManager instance used by this class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RunCat365.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// <summary> + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// </summary> + internal static System.Drawing.Icon AppIcon { + get { + object obj = ResourceManager.GetObject("AppIcon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_0 { + get { + object obj = ResourceManager.GetObject("cat_0", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_1 { + get { + object obj = ResourceManager.GetObject("cat_1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_2 { + get { + object obj = ResourceManager.GetObject("cat_2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_3 { + get { + object obj = ResourceManager.GetObject("cat_3", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_4 { + get { + object obj = ResourceManager.GetObject("cat_4", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_jumping_0 { + get { + object obj = ResourceManager.GetObject("cat_jumping_0", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_jumping_1 { + get { + object obj = ResourceManager.GetObject("cat_jumping_1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_jumping_2 { + get { + object obj = ResourceManager.GetObject("cat_jumping_2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_jumping_3 { + get { + object obj = ResourceManager.GetObject("cat_jumping_3", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_jumping_4 { + get { + object obj = ResourceManager.GetObject("cat_jumping_4", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_jumping_5 { + get { + object obj = ResourceManager.GetObject("cat_jumping_5", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_jumping_6 { + get { + object obj = ResourceManager.GetObject("cat_jumping_6", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_jumping_7 { + get { + object obj = ResourceManager.GetObject("cat_jumping_7", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_jumping_8 { + get { + object obj = ResourceManager.GetObject("cat_jumping_8", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_jumping_9 { + get { + object obj = ResourceManager.GetObject("cat_jumping_9", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_running_0 { + get { + object obj = ResourceManager.GetObject("cat_running_0", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_running_1 { + get { + object obj = ResourceManager.GetObject("cat_running_1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_running_2 { + get { + object obj = ResourceManager.GetObject("cat_running_2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_running_3 { + get { + object obj = ResourceManager.GetObject("cat_running_3", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap cat_running_4 { + get { + object obj = ResourceManager.GetObject("cat_running_4", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap horse_0 { + get { + object obj = ResourceManager.GetObject("horse_0", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap horse_1 { + get { + object obj = ResourceManager.GetObject("horse_1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap horse_2 { + get { + object obj = ResourceManager.GetObject("horse_2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap horse_3 { + get { + object obj = ResourceManager.GetObject("horse_3", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap horse_4 { + get { + object obj = ResourceManager.GetObject("horse_4", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap parrot_0 { + get { + object obj = ResourceManager.GetObject("parrot_0", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap parrot_1 { + get { + object obj = ResourceManager.GetObject("parrot_1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap parrot_2 { + get { + object obj = ResourceManager.GetObject("parrot_2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap parrot_3 { + get { + object obj = ResourceManager.GetObject("parrot_3", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap parrot_4 { + get { + object obj = ResourceManager.GetObject("parrot_4", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap parrot_5 { + get { + object obj = ResourceManager.GetObject("parrot_5", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap parrot_6 { + get { + object obj = ResourceManager.GetObject("parrot_6", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap parrot_7 { + get { + object obj = ResourceManager.GetObject("parrot_7", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap parrot_8 { + get { + object obj = ResourceManager.GetObject("parrot_8", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap parrot_9 { + get { + object obj = ResourceManager.GetObject("parrot_9", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap road_crater { + get { + object obj = ResourceManager.GetObject("road_crater", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap road_flat { + get { + object obj = ResourceManager.GetObject("road_flat", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap road_hill { + get { + object obj = ResourceManager.GetObject("road_hill", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap road_sprout { + get { + object obj = ResourceManager.GetObject("road_sprout", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap slider_rabbit { + get { + object obj = ResourceManager.GetObject("slider_rabbit", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// <summary> + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// </summary> + internal static System.Drawing.Bitmap slider_turtle { + get { + object obj = ResourceManager.GetObject("slider_turtle", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/RunCat365/Properties/Resources.resx b/RunCat365/Properties/Resources.resx new file mode 100644 index 0000000..2820e2e --- /dev/null +++ b/RunCat365/Properties/Resources.resx @@ -0,0 +1,247 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <data name="AppIcon" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\app_icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_jumping_0" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_jumping_0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_jumping_1" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_jumping_1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_jumping_2" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_jumping_2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_jumping_3" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_jumping_3.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_jumping_4" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_jumping_4.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_jumping_5" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_jumping_5.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_jumping_6" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_jumping_6.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_jumping_7" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_jumping_7.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_jumping_8" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_jumping_8.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_jumping_9" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_jumping_9.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_running_0" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_running_0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_running_1" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_running_1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_running_2" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_running_2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_running_3" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_running_3.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_running_4" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\cat_running_4.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="road_crater" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\road_crater.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="road_flat" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\road_flat.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="road_hill" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\road_hill.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="road_sprout" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\game\road_sprout.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_0" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\cat\cat_0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_1" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\cat\cat_1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_2" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\cat\cat_2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_3" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\cat\cat_3.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="cat_4" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\cat\cat_4.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="horse_0" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\horse\horse_0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="horse_1" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\horse\horse_1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="horse_2" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\horse\horse_2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="horse_3" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\horse\horse_3.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="horse_4" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\horse\horse_4.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="parrot_0" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\parrot\parrot_0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="parrot_1" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\parrot\parrot_1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="parrot_2" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\parrot\parrot_2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="parrot_3" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\parrot\parrot_3.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="parrot_4" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\parrot\parrot_4.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="parrot_5" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\parrot\parrot_5.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="parrot_6" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\parrot\parrot_6.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="parrot_7" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\parrot\parrot_7.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="parrot_8" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\parrot\parrot_8.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="parrot_9" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\runners\parrot\parrot_9.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="slider_rabbit" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\rabbit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="slider_turtle" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\resources\turtle.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> +</root> \ No newline at end of file diff --git a/RunCat365/Properties/Strings.Designer.cs b/RunCat365/Properties/Strings.Designer.cs new file mode 100644 index 0000000..3b8db2c --- /dev/null +++ b/RunCat365/Properties/Strings.Designer.cs @@ -0,0 +1,531 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace RunCat365.Properties { + using System; + + + /// <summary> + /// A strongly-typed resource class, for looking up localized strings, etc. + /// </summary> + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Strings() { + } + + /// <summary> + /// Returns the cached ResourceManager instance used by this class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RunCat365.Properties.Strings", typeof(Strings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// <summary> + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// </summary> + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// <summary> + /// Looks up a localized string similar to GAME OVER. + /// </summary> + internal static string Game_GameOver { + get { + return ResourceManager.GetString("Game_GameOver", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Press space to play.. + /// </summary> + internal static string Game_PressSpaceToPlay { + get { + return ResourceManager.GetString("Game_PressSpaceToPlay", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to New Record. + /// </summary> + internal static string Game_NewRecord + { + get + { + return ResourceManager.GetString("Game_NewRecord", resourceCulture); + } + } + /// <summary> + /// Looks up a localized string similar to Endless Game. + /// </summary> + internal static string Menu_EndlessGame { + get { + return ResourceManager.GetString("Menu_EndlessGame", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Exit. + /// </summary> + internal static string Menu_Exit { + get { + return ResourceManager.GetString("Menu_Exit", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to FPS Max Limit. + /// </summary> + internal static string Menu_FPSMaxLimit { + get { + return ResourceManager.GetString("Menu_FPSMaxLimit", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Information. + /// </summary> + internal static string Menu_Information { + get { + return ResourceManager.GetString("Menu_Information", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Launch at startup. + /// </summary> + internal static string Menu_LaunchAtStartup { + get { + return ResourceManager.GetString("Menu_LaunchAtStartup", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Open Project Page. + /// </summary> + internal static string Menu_OpenProjectPage { + get { + return ResourceManager.GetString("Menu_OpenProjectPage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Runner. + /// </summary> + internal static string Menu_Runner { + get { + return ResourceManager.GetString("Menu_Runner", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Settings. + /// </summary> + internal static string Menu_Settings { + get { + return ResourceManager.GetString("Menu_Settings", resourceCulture); + } + } + + internal static string Menu_SpeedSource { + get { + return ResourceManager.GetString("Menu_SpeedSource", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Theme. + /// </summary> + internal static string Menu_Theme { + get { + return ResourceManager.GetString("Menu_Theme", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to App has launched. If the icon is not on the taskbar, it has been omitted, so please move it manually and pin it.. + /// </summary> + internal static string Message_AppLaunched { + get { + return ResourceManager.GetString("Message_AppLaunched", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Failed to get CPU usage. Unable to update running speed.. + /// </summary> + internal static string Message_CPUUsageUnavailable { + get { + return ResourceManager.GetString("Message_CPUUsageUnavailable", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Warning. + /// </summary> + internal static string Message_Warning { + get { + return ResourceManager.GetString("Message_Warning", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Cat. + /// </summary> + internal static string Runner_Cat { + get { + return ResourceManager.GetString("Runner_Cat", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Horse. + /// </summary> + internal static string Runner_Horse { + get { + return ResourceManager.GetString("Runner_Horse", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Parrot. + /// </summary> + internal static string Runner_Parrot { + get { + return ResourceManager.GetString("Runner_Parrot", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Dark. + /// </summary> + internal static string Theme_Dark { + get { + return ResourceManager.GetString("Theme_Dark", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Light. + /// </summary> + internal static string Theme_Light { + get { + return ResourceManager.GetString("Theme_Light", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to System. + /// </summary> + internal static string Theme_System { + get { + return ResourceManager.GetString("Theme_System", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Endless Game. + /// </summary> + internal static string Window_EndlessGame { + get { + return ResourceManager.GetString("Window_EndlessGame", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to CPU. + /// </summary> + internal static string SystemInfo_CPU { + get { + return ResourceManager.GetString("SystemInfo_CPU", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to User. + /// </summary> + internal static string SystemInfo_User { + get { + return ResourceManager.GetString("SystemInfo_User", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Kernel. + /// </summary> + internal static string SystemInfo_Kernel { + get { + return ResourceManager.GetString("SystemInfo_Kernel", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Available. + /// </summary> + internal static string SystemInfo_Available { + get { + return ResourceManager.GetString("SystemInfo_Available", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Memory. + /// </summary> + internal static string SystemInfo_Memory { + get { + return ResourceManager.GetString("SystemInfo_Memory", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Total. + /// </summary> + internal static string SystemInfo_Total { + get { + return ResourceManager.GetString("SystemInfo_Total", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Used. + /// </summary> + internal static string SystemInfo_Used { + get { + return ResourceManager.GetString("SystemInfo_Used", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Storage. + /// </summary> + internal static string SystemInfo_Storage { + get { + return ResourceManager.GetString("SystemInfo_Storage", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to C Drive. + /// </summary> + internal static string SystemInfo_DriveC { + get { + return ResourceManager.GetString("SystemInfo_DriveC", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to D Drive. + /// </summary> + internal static string SystemInfo_DriveD { + get { + return ResourceManager.GetString("SystemInfo_DriveD", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Network. + /// </summary> + internal static string SystemInfo_Network { + get { + return ResourceManager.GetString("SystemInfo_Network", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Sent. + /// </summary> + internal static string SystemInfo_Sent { + get { + return ResourceManager.GetString("SystemInfo_Sent", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Received. + /// </summary> + internal static string SystemInfo_Received { + get { + return ResourceManager.GetString("SystemInfo_Received", resourceCulture); + } + } + + internal static string SystemInfo_GPU { + get { + return ResourceManager.GetString("SystemInfo_GPU", resourceCulture); + } + } + + internal static string SystemInfo_Average { + get { + return ResourceManager.GetString("SystemInfo_Average", resourceCulture); + } + } + + internal static string SystemInfo_Maximum { + get { + return ResourceManager.GetString("SystemInfo_Maximum", resourceCulture); + } + } + + internal static string SystemInfo_Temperature { + get { + return ResourceManager.GetString("SystemInfo_Temperature", resourceCulture); + } + } + + internal static string SystemInfo_TemperatureCelsiusFormat { + get { + return ResourceManager.GetString("SystemInfo_TemperatureCelsiusFormat", resourceCulture); + } + } + + internal static string SystemInfo_TemperatureFahrenheitFormat { + get { + return ResourceManager.GetString("SystemInfo_TemperatureFahrenheitFormat", resourceCulture); + } + } + + internal static string Menu_CustomRunners { + get { + return ResourceManager.GetString("Menu_CustomRunners", resourceCulture); + } + } + + internal static string Window_CustomRunners { + get { + return ResourceManager.GetString("Window_CustomRunners", resourceCulture); + } + } + + internal static string CustomRunner_AddedRunners { + get { + return ResourceManager.GetString("CustomRunner_AddedRunners", resourceCulture); + } + } + + internal static string CustomRunner_Name { + get { + return ResourceManager.GetString("CustomRunner_Name", resourceCulture); + } + } + + internal static string CustomRunner_AddFrames { + get { + return ResourceManager.GetString("CustomRunner_AddFrames", resourceCulture); + } + } + + internal static string CustomRunner_RemoveFrame { + get { + return ResourceManager.GetString("CustomRunner_RemoveFrame", resourceCulture); + } + } + + internal static string CustomRunner_Save { + get { + return ResourceManager.GetString("CustomRunner_Save", resourceCulture); + } + } + + internal static string CustomRunner_Delete { + get { + return ResourceManager.GetString("CustomRunner_Delete", resourceCulture); + } + } + + internal static string CustomRunner_Requirements { + get { + return ResourceManager.GetString("CustomRunner_Requirements", resourceCulture); + } + } + + internal static string CustomRunner_ConfirmOverwrite { + get { + return ResourceManager.GetString("CustomRunner_ConfirmOverwrite", resourceCulture); + } + } + + internal static string CustomRunner_ConfirmDelete { + get { + return ResourceManager.GetString("CustomRunner_ConfirmDelete", resourceCulture); + } + } + + internal static string CustomRunner_RequirementsLabel { + get { + return ResourceManager.GetString("CustomRunner_RequirementsLabel", resourceCulture); + } + } + + internal static string CustomRunner_FramesLabel { + get { + return ResourceManager.GetString("CustomRunner_FramesLabel", resourceCulture); + } + } + + internal static string CustomRunner_Preview { + get { + return ResourceManager.GetString("CustomRunner_Preview", resourceCulture); + } + } + + internal static string CustomRunner_PreviewSpeed { + get { + return ResourceManager.GetString("CustomRunner_PreviewSpeed", resourceCulture); + } + } + + internal static string Menu_TemperatureUnit { + get { + return ResourceManager.GetString("Menu_TemperatureUnit", resourceCulture); + } + } + + internal static string TemperatureUnit_System { + get { + return ResourceManager.GetString("TemperatureUnit_System", resourceCulture); + } + } + + internal static string TemperatureUnit_Celsius { + get { + return ResourceManager.GetString("TemperatureUnit_Celsius", resourceCulture); + } + } + + internal static string TemperatureUnit_Fahrenheit { + get { + return ResourceManager.GetString("TemperatureUnit_Fahrenheit", resourceCulture); + } + } + } +} diff --git a/RunCat365/Properties/Strings.de.resx b/RunCat365/Properties/Strings.de.resx new file mode 100644 index 0000000..41a13c4 --- /dev/null +++ b/RunCat365/Properties/Strings.de.resx @@ -0,0 +1,303 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Menu_Runner" xml:space="preserve"> + <value>Läufer</value> + </data> + <data name="Menu_Theme" xml:space="preserve"> + <value>Design</value> + </data> + <data name="Menu_FPSMaxLimit" xml:space="preserve"> + <value>FPS-Maximallimit</value> + </data> + <data name="Menu_LaunchAtStartup" xml:space="preserve"> + <value>Beim Start ausführen</value> + </data> + <data name="Menu_Settings" xml:space="preserve"> + <value>Einstellungen</value> + </data> + <data name="Menu_EndlessGame" xml:space="preserve"> + <value>Endlosspiel</value> + </data> + <data name="Menu_Information" xml:space="preserve"> + <value>Information</value> + </data> + <data name="Menu_OpenProjectPage" xml:space="preserve"> + <value>Projektseite öffnen</value> + </data> + <data name="Menu_Exit" xml:space="preserve"> + <value>Beenden</value> + </data> + <data name="Theme_System" xml:space="preserve"> + <value>System</value> + </data> + <data name="Theme_Light" xml:space="preserve"> + <value>Hell</value> + </data> + <data name="Theme_Dark" xml:space="preserve"> + <value>Dunkel</value> + </data> + <data name="Menu_TemperatureUnit" xml:space="preserve"> + <value>Temperatureinheit</value> + </data> + <data name="TemperatureUnit_System" xml:space="preserve"> + <value>System</value> + </data> + <data name="TemperatureUnit_Celsius" xml:space="preserve"> + <value>Celsius</value> + </data> + <data name="TemperatureUnit_Fahrenheit" xml:space="preserve"> + <value>Fahrenheit</value> + </data> + <data name="Runner_Cat" xml:space="preserve"> + <value>Katze</value> + </data> + <data name="Runner_Parrot" xml:space="preserve"> + <value>Papagei</value> + </data> + <data name="Runner_Horse" xml:space="preserve"> + <value>Pferd</value> + </data> + <data name="Message_AppLaunched" xml:space="preserve"> + <value>Die App wurde gestartet. Wenn das Symbol nicht in der Taskleiste angezeigt wird, wurde es ausgeblendet. Bitte verschieben Sie es manuell und heften Sie es an.</value> + </data> + <data name="Message_Warning" xml:space="preserve"> + <value>Warnung</value> + </data> + <data name="Window_EndlessGame" xml:space="preserve"> + <value>Endlosspiel</value> + </data> + <data name="Game_PressSpaceToPlay" xml:space="preserve"> + <value>Leertaste drücken zum Spielen.</value> + </data> + <data name="Game_GameOver" xml:space="preserve"> + <value>SPIEL VORBEI</value> + </data> + <data name="SystemInfo_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="SystemInfo_User" xml:space="preserve"> + <value>Benutzer</value> + </data> + <data name="SystemInfo_Kernel" xml:space="preserve"> + <value>Kernel</value> + </data> + <data name="SystemInfo_Available" xml:space="preserve"> + <value>Verfügbar</value> + </data> + <data name="SystemInfo_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="SystemInfo_Average" xml:space="preserve"> + <value>Durchschnitt</value> + </data> + <data name="SystemInfo_Maximum" xml:space="preserve"> + <value>Maximum</value> + </data> + <data name="SystemInfo_Memory" xml:space="preserve"> + <value>Arbeitsspeicher</value> + </data> + <data name="SystemInfo_Total" xml:space="preserve"> + <value>Gesamt</value> + </data> + <data name="SystemInfo_Used" xml:space="preserve"> + <value>Verwendet</value> + </data> + <data name="SystemInfo_Storage" xml:space="preserve"> + <value>Speicher</value> + </data> + <data name="SystemInfo_DriveC" xml:space="preserve"> + <value>Laufwerk C</value> + </data> + <data name="SystemInfo_DriveD" xml:space="preserve"> + <value>Laufwerk D</value> + </data> + <data name="SystemInfo_Network" xml:space="preserve"> + <value>Netzwerk</value> + </data> + <data name="SystemInfo_Sent" xml:space="preserve"> + <value>Gesendet</value> + </data> + <data name="SystemInfo_Received" xml:space="preserve"> + <value>Empfangen</value> + </data> + <data name="Message_CPUUsageUnavailable" xml:space="preserve"> + <value>CPU-Auslastung konnte nicht abgerufen werden. Die Laufgeschwindigkeit kann nicht aktualisiert werden.</value> + </data> + <data name="Menu_SpeedSource" xml:space="preserve"> + <value>Geschwindigkeit basierend auf</value> + </data> + <data name="Game_NewRecord" xml:space="preserve"> + <value>Neuer Rekord</value> + </data> + <data name="SystemInfo_Temperature" xml:space="preserve"> + <value>Temperatur</value> + </data> + <data name="SystemInfo_TemperatureCelsiusFormat" xml:space="preserve"> + <value>{0:f1}°C</value> + </data> + <data name="SystemInfo_TemperatureFahrenheitFormat" xml:space="preserve"> + <value>{0:f1}°F</value> + </data> + <data name="Menu_CustomRunners" xml:space="preserve"> + <value>Benutzerdefinierte Runner...</value> + </data> + <data name="Window_CustomRunners" xml:space="preserve"> + <value>Benutzerdefinierte Runner</value> + </data> + <data name="CustomRunner_AddedRunners" xml:space="preserve"> + <value>Benutzerdefinierte Runner:</value> + </data> + <data name="CustomRunner_Name" xml:space="preserve"> + <value>Runner-Name:</value> + </data> + <data name="CustomRunner_AddFrames" xml:space="preserve"> + <value>Frames hinzufügen...</value> + </data> + <data name="CustomRunner_RemoveFrame" xml:space="preserve"> + <value>Frame entfernen</value> + </data> + <data name="CustomRunner_Save" xml:space="preserve"> + <value>Speichern</value> + </data> + <data name="CustomRunner_Delete" xml:space="preserve"> + <value>Entfernen</value> + </data> + <data name="CustomRunner_Requirements" xml:space="preserve"> + <value>Format: PNG | Max Abmessungen: 32x32px</value> + </data> + <data name="CustomRunner_ConfirmOverwrite" xml:space="preserve"> + <value>Ein Runner mit diesem Namen existiert bereits. Überschreiben?</value> + </data> + <data name="CustomRunner_ConfirmDelete" xml:space="preserve"> + <value>Runner "{0}" löschen?</value> + </data> + <data name="CustomRunner_RequirementsLabel" xml:space="preserve"> + <value>Anforderungen:</value> + </data> + <data name="CustomRunner_FramesLabel" xml:space="preserve"> + <value>Frames:</value> + </data> + <data name="CustomRunner_Preview" xml:space="preserve"> + <value>Vorschau:</value> + </data> + <data name="CustomRunner_PreviewSpeed" xml:space="preserve"> + <value>Vorschaugeschwindigkeit:</value> + </data> +</root> \ No newline at end of file diff --git a/RunCat365/Properties/Strings.es.resx b/RunCat365/Properties/Strings.es.resx new file mode 100644 index 0000000..2e16b4f --- /dev/null +++ b/RunCat365/Properties/Strings.es.resx @@ -0,0 +1,303 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Menu_Runner" xml:space="preserve"> + <value>Corredor</value> + </data> + <data name="Menu_Theme" xml:space="preserve"> + <value>Tema</value> + </data> + <data name="Menu_FPSMaxLimit" xml:space="preserve"> + <value>Límite Máximo de FPS</value> + </data> + <data name="Menu_LaunchAtStartup" xml:space="preserve"> + <value>Ejecutar al inicio</value> + </data> + <data name="Menu_Settings" xml:space="preserve"> + <value>Ajustes</value> + </data> + <data name="Menu_EndlessGame" xml:space="preserve"> + <value>Juego Sin Fin</value> + </data> + <data name="Menu_Information" xml:space="preserve"> + <value>Información</value> + </data> + <data name="Menu_OpenProjectPage" xml:space="preserve"> + <value>Abrir página del proyecto</value> + </data> + <data name="Menu_Exit" xml:space="preserve"> + <value>Salir</value> + </data> + <data name="Theme_System" xml:space="preserve"> + <value>Sistema</value> + </data> + <data name="Theme_Light" xml:space="preserve"> + <value>Claro</value> + </data> + <data name="Theme_Dark" xml:space="preserve"> + <value>Oscuro</value> + </data> + <data name="Menu_TemperatureUnit" xml:space="preserve"> + <value>Unidad de temperatura</value> + </data> + <data name="TemperatureUnit_System" xml:space="preserve"> + <value>Sistema</value> + </data> + <data name="TemperatureUnit_Celsius" xml:space="preserve"> + <value>Celsius</value> + </data> + <data name="TemperatureUnit_Fahrenheit" xml:space="preserve"> + <value>Fahrenheit</value> + </data> + <data name="Runner_Cat" xml:space="preserve"> + <value>Gato</value> + </data> + <data name="Runner_Parrot" xml:space="preserve"> + <value>Loro</value> + </data> + <data name="Runner_Horse" xml:space="preserve"> + <value>Caballo</value> + </data> + <data name="Message_AppLaunched" xml:space="preserve"> + <value>La aplicación se ha iniciado. Si el ícono no aparece en la barra de tareas, puede estar oculto; por favor muévelo manualmente y fíjalo.</value> + </data> + <data name="Message_Warning" xml:space="preserve"> + <value>Advertencia</value> + </data> + <data name="Window_EndlessGame" xml:space="preserve"> + <value>Juego Sin Fin</value> + </data> + <data name="Game_PressSpaceToPlay" xml:space="preserve"> + <value>Presiona espacio para jugar.</value> + </data> + <data name="Game_GameOver" xml:space="preserve"> + <value>FIN DEL JUEGO</value> + </data> + <data name="SystemInfo_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="SystemInfo_User" xml:space="preserve"> + <value>Usuario</value> + </data> + <data name="SystemInfo_Kernel" xml:space="preserve"> + <value>Núcleo</value> + </data> + <data name="SystemInfo_Available" xml:space="preserve"> + <value>Disponible</value> + </data> + <data name="SystemInfo_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="SystemInfo_Average" xml:space="preserve"> + <value>Promedio</value> + </data> + <data name="SystemInfo_Maximum" xml:space="preserve"> + <value>Máximo</value> + </data> + <data name="SystemInfo_Memory" xml:space="preserve"> + <value>Memoria</value> + </data> + <data name="SystemInfo_Total" xml:space="preserve"> + <value>Total</value> + </data> + <data name="SystemInfo_Used" xml:space="preserve"> + <value>Usado</value> + </data> + <data name="SystemInfo_Storage" xml:space="preserve"> + <value>Almacenamiento</value> + </data> + <data name="SystemInfo_DriveC" xml:space="preserve"> + <value>Disco C</value> + </data> + <data name="SystemInfo_DriveD" xml:space="preserve"> + <value>Disco D</value> + </data> + <data name="SystemInfo_Network" xml:space="preserve"> + <value>Red</value> + </data> + <data name="SystemInfo_Sent" xml:space="preserve"> + <value>Enviado</value> + </data> + <data name="SystemInfo_Received" xml:space="preserve"> + <value>Recibido</value> + </data> + <data name="Message_CPUUsageUnavailable" xml:space="preserve"> + <value>No se pudo obtener el uso de la CPU. No es posible actualizar la velocidad del corredor.</value> + </data> + <data name="Menu_SpeedSource" xml:space="preserve"> + <value>Velocidad basada en</value> + </data> + <data name="Game_NewRecord" xml:space="preserve"> + <value>Nuevo Récord</value> + </data> + <data name="SystemInfo_Temperature" xml:space="preserve"> + <value>Temperatura</value> + </data> + <data name="SystemInfo_TemperatureCelsiusFormat" xml:space="preserve"> + <value>{0:f1}°C</value> + </data> + <data name="SystemInfo_TemperatureFahrenheitFormat" xml:space="preserve"> + <value>{0:f1}°F</value> + </data> + <data name="Menu_CustomRunners" xml:space="preserve"> + <value>Runners personalizados...</value> + </data> + <data name="Window_CustomRunners" xml:space="preserve"> + <value>Runners personalizados</value> + </data> + <data name="CustomRunner_AddedRunners" xml:space="preserve"> + <value>Runners personalizados:</value> + </data> + <data name="CustomRunner_Name" xml:space="preserve"> + <value>Nombre del runner:</value> + </data> + <data name="CustomRunner_AddFrames" xml:space="preserve"> + <value>Añadir fotogramas...</value> + </data> + <data name="CustomRunner_RemoveFrame" xml:space="preserve"> + <value>Eliminar fotograma</value> + </data> + <data name="CustomRunner_Save" xml:space="preserve"> + <value>Guardar</value> + </data> + <data name="CustomRunner_Delete" xml:space="preserve"> + <value>Eliminar</value> + </data> + <data name="CustomRunner_Requirements" xml:space="preserve"> + <value>Formato: PNG | Dimensiones máx: 32x32px</value> + </data> + <data name="CustomRunner_ConfirmOverwrite" xml:space="preserve"> + <value>Ya existe un runner con este nombre. ¿Sobrescribir?</value> + </data> + <data name="CustomRunner_ConfirmDelete" xml:space="preserve"> + <value>¿Eliminar runner "{0}"?</value> + </data> + <data name="CustomRunner_RequirementsLabel" xml:space="preserve"> + <value>Requisitos:</value> + </data> + <data name="CustomRunner_FramesLabel" xml:space="preserve"> + <value>Fotogramas:</value> + </data> + <data name="CustomRunner_Preview" xml:space="preserve"> + <value>Vista previa:</value> + </data> + <data name="CustomRunner_PreviewSpeed" xml:space="preserve"> + <value>Velocidad de vista previa:</value> + </data> +</root> \ No newline at end of file diff --git a/RunCat365/Properties/Strings.fr.resx b/RunCat365/Properties/Strings.fr.resx new file mode 100644 index 0000000..eb9a23c --- /dev/null +++ b/RunCat365/Properties/Strings.fr.resx @@ -0,0 +1,303 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Menu_Runner" xml:space="preserve"> + <value>Coureur</value> + </data> + <data name="Menu_Theme" xml:space="preserve"> + <value>Thème</value> + </data> + <data name="Menu_FPSMaxLimit" xml:space="preserve"> + <value>Limite FPS maximale</value> + </data> + <data name="Menu_LaunchAtStartup" xml:space="preserve"> + <value>Lancer au démarrage</value> + </data> + <data name="Menu_Settings" xml:space="preserve"> + <value>Paramètres</value> + </data> + <data name="Menu_EndlessGame" xml:space="preserve"> + <value>Jeu sans fin</value> + </data> + <data name="Menu_Information" xml:space="preserve"> + <value>Informations</value> + </data> + <data name="Menu_OpenProjectPage" xml:space="preserve"> + <value>Ouvrir la page du projet</value> + </data> + <data name="Menu_Exit" xml:space="preserve"> + <value>Quitter</value> + </data> + <data name="Theme_System" xml:space="preserve"> + <value>Système</value> + </data> + <data name="Theme_Light" xml:space="preserve"> + <value>Clair</value> + </data> + <data name="Theme_Dark" xml:space="preserve"> + <value>Sombre</value> + </data> + <data name="Menu_TemperatureUnit" xml:space="preserve"> + <value>Unité de température</value> + </data> + <data name="TemperatureUnit_System" xml:space="preserve"> + <value>Système</value> + </data> + <data name="TemperatureUnit_Celsius" xml:space="preserve"> + <value>Celsius</value> + </data> + <data name="TemperatureUnit_Fahrenheit" xml:space="preserve"> + <value>Fahrenheit</value> + </data> + <data name="Runner_Cat" xml:space="preserve"> + <value>Chat</value> + </data> + <data name="Runner_Parrot" xml:space="preserve"> + <value>Perroquet</value> + </data> + <data name="Runner_Horse" xml:space="preserve"> + <value>Cheval</value> + </data> + <data name="Message_AppLaunched" xml:space="preserve"> + <value>L'application a démarré. Si l'icône n'apparaît pas dans la barre des tâches, elle a peut-être été masquée ; veuillez la déplacer manuellement et l'épingler.</value> + </data> + <data name="Message_Warning" xml:space="preserve"> + <value>Avertissement</value> + </data> + <data name="Window_EndlessGame" xml:space="preserve"> + <value>Jeu sans fin</value> + </data> + <data name="Game_PressSpaceToPlay" xml:space="preserve"> + <value>Appuyez sur espace pour jouer.</value> + </data> + <data name="Game_GameOver" xml:space="preserve"> + <value>GAME OVER</value> + </data> + <data name="SystemInfo_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="SystemInfo_User" xml:space="preserve"> + <value>Utilisateur</value> + </data> + <data name="SystemInfo_Kernel" xml:space="preserve"> + <value>Noyau</value> + </data> + <data name="SystemInfo_Available" xml:space="preserve"> + <value>Disponible</value> + </data> + <data name="SystemInfo_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="SystemInfo_Average" xml:space="preserve"> + <value>Moyenne</value> + </data> + <data name="SystemInfo_Maximum" xml:space="preserve"> + <value>Maximum</value> + </data> + <data name="SystemInfo_Memory" xml:space="preserve"> + <value>Mémoire</value> + </data> + <data name="SystemInfo_Total" xml:space="preserve"> + <value>Total</value> + </data> + <data name="SystemInfo_Used" xml:space="preserve"> + <value>Utilisé</value> + </data> + <data name="SystemInfo_Storage" xml:space="preserve"> + <value>Stockage</value> + </data> + <data name="SystemInfo_DriveC" xml:space="preserve"> + <value>Disque C</value> + </data> + <data name="SystemInfo_DriveD" xml:space="preserve"> + <value>Disque D</value> + </data> + <data name="SystemInfo_Network" xml:space="preserve"> + <value>Réseau</value> + </data> + <data name="SystemInfo_Sent" xml:space="preserve"> + <value>Envoyé</value> + </data> + <data name="SystemInfo_Received" xml:space="preserve"> + <value>Reçu</value> + </data> + <data name="Message_CPUUsageUnavailable" xml:space="preserve"> + <value>Impossible d'obtenir l'utilisation du CPU. Impossible de mettre à jour la vitesse du coureur.</value> + </data> + <data name="Menu_SpeedSource" xml:space="preserve"> + <value>Vitesse basée sur</value> + </data> + <data name="Game_NewRecord" xml:space="preserve"> + <value>Nouveau record</value> + </data> + <data name="SystemInfo_Temperature" xml:space="preserve"> + <value>Température</value> + </data> + <data name="SystemInfo_TemperatureCelsiusFormat" xml:space="preserve"> + <value>{0:f1}°C</value> + </data> + <data name="SystemInfo_TemperatureFahrenheitFormat" xml:space="preserve"> + <value>{0:f1}°F</value> + </data> + <data name="Menu_CustomRunners" xml:space="preserve"> + <value>Runners personnalisés...</value> + </data> + <data name="Window_CustomRunners" xml:space="preserve"> + <value>Runners personnalisés</value> + </data> + <data name="CustomRunner_AddedRunners" xml:space="preserve"> + <value>Runners personnalisés :</value> + </data> + <data name="CustomRunner_Name" xml:space="preserve"> + <value>Nom du runner :</value> + </data> + <data name="CustomRunner_AddFrames" xml:space="preserve"> + <value>Ajouter des images...</value> + </data> + <data name="CustomRunner_RemoveFrame" xml:space="preserve"> + <value>Supprimer l'image</value> + </data> + <data name="CustomRunner_Save" xml:space="preserve"> + <value>Enregistrer</value> + </data> + <data name="CustomRunner_Delete" xml:space="preserve"> + <value>Supprimer</value> + </data> + <data name="CustomRunner_Requirements" xml:space="preserve"> + <value>Format : PNG | Dimensions max : 32x32px</value> + </data> + <data name="CustomRunner_ConfirmOverwrite" xml:space="preserve"> + <value>Un runner avec ce nom existe déjà. Écraser ?</value> + </data> + <data name="CustomRunner_ConfirmDelete" xml:space="preserve"> + <value>Supprimer le runner « {0} » ?</value> + </data> + <data name="CustomRunner_RequirementsLabel" xml:space="preserve"> + <value>Exigences :</value> + </data> + <data name="CustomRunner_FramesLabel" xml:space="preserve"> + <value>Images :</value> + </data> + <data name="CustomRunner_Preview" xml:space="preserve"> + <value>Aperçu :</value> + </data> + <data name="CustomRunner_PreviewSpeed" xml:space="preserve"> + <value>Vitesse d'aperçu :</value> + </data> +</root> \ No newline at end of file diff --git a/RunCat365/Properties/Strings.ja.resx b/RunCat365/Properties/Strings.ja.resx new file mode 100644 index 0000000..12b8e56 --- /dev/null +++ b/RunCat365/Properties/Strings.ja.resx @@ -0,0 +1,303 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Menu_Runner" xml:space="preserve"> + <value>ランナー</value> + </data> + <data name="Menu_Theme" xml:space="preserve"> + <value>テーマ</value> + </data> + <data name="Menu_FPSMaxLimit" xml:space="preserve"> + <value>FPSの上限</value> + </data> + <data name="Menu_LaunchAtStartup" xml:space="preserve"> + <value>スタートアップ時に起動</value> + </data> + <data name="Menu_Settings" xml:space="preserve"> + <value>設定</value> + </data> + <data name="Menu_EndlessGame" xml:space="preserve"> + <value>エンドレスゲーム</value> + </data> + <data name="Menu_Information" xml:space="preserve"> + <value>情報</value> + </data> + <data name="Menu_OpenProjectPage" xml:space="preserve"> + <value>プロジェクトページを開く</value> + </data> + <data name="Menu_Exit" xml:space="preserve"> + <value>終了</value> + </data> + <data name="Theme_System" xml:space="preserve"> + <value>システム</value> + </data> + <data name="Theme_Light" xml:space="preserve"> + <value>ライト</value> + </data> + <data name="Theme_Dark" xml:space="preserve"> + <value>ダーク</value> + </data> + <data name="Menu_TemperatureUnit" xml:space="preserve"> + <value>温度単位</value> + </data> + <data name="TemperatureUnit_System" xml:space="preserve"> + <value>システム</value> + </data> + <data name="TemperatureUnit_Celsius" xml:space="preserve"> + <value>摂氏</value> + </data> + <data name="TemperatureUnit_Fahrenheit" xml:space="preserve"> + <value>華氏</value> + </data> + <data name="Runner_Cat" xml:space="preserve"> + <value>ネコ</value> + </data> + <data name="Runner_Parrot" xml:space="preserve"> + <value>オウム</value> + </data> + <data name="Runner_Horse" xml:space="preserve"> + <value>早馬</value> + </data> + <data name="Message_AppLaunched" xml:space="preserve"> + <value>アプリが起動しました。タスクバーにアイコンが表示されていない場合は、省略されているので手動で移動してピン留めしてください。</value> + </data> + <data name="Message_Warning" xml:space="preserve"> + <value>警告</value> + </data> + <data name="Window_EndlessGame" xml:space="preserve"> + <value>エンドレスゲーム</value> + </data> + <data name="Game_PressSpaceToPlay" xml:space="preserve"> + <value>スペースキーでスタート</value> + </data> + <data name="Game_GameOver" xml:space="preserve"> + <value>ゲームオーバー</value> + </data> + <data name="SystemInfo_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="SystemInfo_User" xml:space="preserve"> + <value>ユーザー</value> + </data> + <data name="SystemInfo_Kernel" xml:space="preserve"> + <value>カーネル</value> + </data> + <data name="SystemInfo_Available" xml:space="preserve"> + <value>使用可能</value> + </data> + <data name="SystemInfo_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="SystemInfo_Average" xml:space="preserve"> + <value>平均</value> + </data> + <data name="SystemInfo_Maximum" xml:space="preserve"> + <value>最大</value> + </data> + <data name="SystemInfo_Memory" xml:space="preserve"> + <value>メモリ</value> + </data> + <data name="SystemInfo_Total" xml:space="preserve"> + <value>合計</value> + </data> + <data name="SystemInfo_Used" xml:space="preserve"> + <value>使用中</value> + </data> + <data name="SystemInfo_Storage" xml:space="preserve"> + <value>ストレージ</value> + </data> + <data name="SystemInfo_DriveC" xml:space="preserve"> + <value>Cドライブ</value> + </data> + <data name="SystemInfo_DriveD" xml:space="preserve"> + <value>Dドライブ</value> + </data> + <data name="SystemInfo_Network" xml:space="preserve"> + <value>ネットワーク</value> + </data> + <data name="SystemInfo_Sent" xml:space="preserve"> + <value>送信</value> + </data> + <data name="SystemInfo_Received" xml:space="preserve"> + <value>受信</value> + </data> + <data name="Message_CPUUsageUnavailable" xml:space="preserve"> + <value>CPU使用率の取得ができません。走る速度を更新できません。</value> + </data> + <data name="Menu_SpeedSource" xml:space="preserve"> + <value>速度の基準</value> + </data> + <data name="Game_NewRecord" xml:space="preserve"> + <value>新記録</value> + </data> + <data name="SystemInfo_Temperature" xml:space="preserve"> + <value>温度</value> + </data> + <data name="SystemInfo_TemperatureCelsiusFormat" xml:space="preserve"> + <value>{0:f1}°C</value> + </data> + <data name="SystemInfo_TemperatureFahrenheitFormat" xml:space="preserve"> + <value>{0:f1}°F</value> + </data> + <data name="Menu_CustomRunners" xml:space="preserve"> + <value>カスタムランナー...</value> + </data> + <data name="Window_CustomRunners" xml:space="preserve"> + <value>カスタムランナー</value> + </data> + <data name="CustomRunner_AddedRunners" xml:space="preserve"> + <value>カスタムランナー:</value> + </data> + <data name="CustomRunner_Name" xml:space="preserve"> + <value>ランナー名:</value> + </data> + <data name="CustomRunner_AddFrames" xml:space="preserve"> + <value>フレーム追加...</value> + </data> + <data name="CustomRunner_RemoveFrame" xml:space="preserve"> + <value>フレームを削除</value> + </data> + <data name="CustomRunner_Save" xml:space="preserve"> + <value>保存</value> + </data> + <data name="CustomRunner_Delete" xml:space="preserve"> + <value>削除</value> + </data> + <data name="CustomRunner_Requirements" xml:space="preserve"> + <value>形式: PNG | 最大サイズ: 32x32px</value> + </data> + <data name="CustomRunner_ConfirmOverwrite" xml:space="preserve"> + <value>同名のランナーが既に存在します。上書きしますか?</value> + </data> + <data name="CustomRunner_ConfirmDelete" xml:space="preserve"> + <value>ランナー「{0}」を削除しますか?</value> + </data> + <data name="CustomRunner_RequirementsLabel" xml:space="preserve"> + <value>要件:</value> + </data> + <data name="CustomRunner_FramesLabel" xml:space="preserve"> + <value>フレーム:</value> + </data> + <data name="CustomRunner_Preview" xml:space="preserve"> + <value>プレビュー:</value> + </data> + <data name="CustomRunner_PreviewSpeed" xml:space="preserve"> + <value>プレビュー速度:</value> + </data> +</root> \ No newline at end of file diff --git a/RunCat365/Properties/Strings.ko.resx b/RunCat365/Properties/Strings.ko.resx new file mode 100644 index 0000000..c009055 --- /dev/null +++ b/RunCat365/Properties/Strings.ko.resx @@ -0,0 +1,303 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Menu_Runner" xml:space="preserve"> + <value>러너</value> + </data> + <data name="Menu_Theme" xml:space="preserve"> + <value>테마</value> + </data> + <data name="Menu_FPSMaxLimit" xml:space="preserve"> + <value>최대 FPS 제한</value> + </data> + <data name="Menu_LaunchAtStartup" xml:space="preserve"> + <value>윈도우 시작 시 자동 실행</value> + </data> + <data name="Menu_Settings" xml:space="preserve"> + <value>설정</value> + </data> + <data name="Menu_EndlessGame" xml:space="preserve"> + <value>무한 게임</value> + </data> + <data name="Menu_Information" xml:space="preserve"> + <value>정보</value> + </data> + <data name="Menu_OpenProjectPage" xml:space="preserve"> + <value>프로젝트 페이지 열기</value> + </data> + <data name="Menu_Exit" xml:space="preserve"> + <value>종료</value> + </data> + <data name="Theme_System" xml:space="preserve"> + <value>시스템 설정 사용</value> + </data> + <data name="Theme_Light" xml:space="preserve"> + <value>라이트 모드</value> + </data> + <data name="Theme_Dark" xml:space="preserve"> + <value>다크 모드</value> + </data> + <data name="Menu_TemperatureUnit" xml:space="preserve"> + <value>온도 단위</value> + </data> + <data name="TemperatureUnit_System" xml:space="preserve"> + <value>시스템</value> + </data> + <data name="TemperatureUnit_Celsius" xml:space="preserve"> + <value>섭씨</value> + </data> + <data name="TemperatureUnit_Fahrenheit" xml:space="preserve"> + <value>화씨</value> + </data> + <data name="Runner_Cat" xml:space="preserve"> + <value>고양이</value> + </data> + <data name="Runner_Parrot" xml:space="preserve"> + <value>앵무새</value> + </data> + <data name="Runner_Horse" xml:space="preserve"> + <value>말</value> + </data> + <data name="Message_AppLaunched" xml:space="preserve"> + <value>앱이 실행되었습니다. 작업 표시줄에 아이콘이 보이지 않는다면 숨겨진 아이콘 영역에 있을 수 있으니, 직접 드래그하여 작업 표시줄에 고정해 주세요.</value> + </data> + <data name="Message_Warning" xml:space="preserve"> + <value>경고</value> + </data> + <data name="Window_EndlessGame" xml:space="preserve"> + <value>무한 게임</value> + </data> + <data name="Game_PressSpaceToPlay" xml:space="preserve"> + <value>스페이스바를 눌러 시작하세요.</value> + </data> + <data name="Game_GameOver" xml:space="preserve"> + <value>게임 오버</value> + </data> + <data name="SystemInfo_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="SystemInfo_User" xml:space="preserve"> + <value>사용자</value> + </data> + <data name="SystemInfo_Kernel" xml:space="preserve"> + <value>커널</value> + </data> + <data name="SystemInfo_Available" xml:space="preserve"> + <value>사용 가능</value> + </data> + <data name="SystemInfo_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="SystemInfo_Average" xml:space="preserve"> + <value>평균</value> + </data> + <data name="SystemInfo_Maximum" xml:space="preserve"> + <value>최대</value> + </data> + <data name="SystemInfo_Memory" xml:space="preserve"> + <value>메모리</value> + </data> + <data name="SystemInfo_Total" xml:space="preserve"> + <value>전체</value> + </data> + <data name="SystemInfo_Used" xml:space="preserve"> + <value>사용 중</value> + </data> + <data name="SystemInfo_Storage" xml:space="preserve"> + <value>저장 장치</value> + </data> + <data name="SystemInfo_DriveC" xml:space="preserve"> + <value>C 드라이브</value> + </data> + <data name="SystemInfo_DriveD" xml:space="preserve"> + <value>D 드라이브</value> + </data> + <data name="SystemInfo_Network" xml:space="preserve"> + <value>네트워크</value> + </data> + <data name="SystemInfo_Sent" xml:space="preserve"> + <value>업로드</value> + </data> + <data name="SystemInfo_Received" xml:space="preserve"> + <value>다운로드</value> + </data> + <data name="Message_CPUUsageUnavailable" xml:space="preserve"> + <value>CPU 사용량을 가져오지 못했습니다. 러너의 달리기 속도를 갱신할 수 없습니다.</value> + </data> + <data name="Menu_SpeedSource" xml:space="preserve"> + <value>달리기 속도 기준</value> + </data> + <data name="Game_NewRecord" xml:space="preserve"> + <value>최고 기록 달성!</value> + </data> + <data name="SystemInfo_Temperature" xml:space="preserve"> + <value>온도</value> + </data> + <data name="SystemInfo_TemperatureCelsiusFormat" xml:space="preserve"> + <value>{0:f1}°C</value> + </data> + <data name="SystemInfo_TemperatureFahrenheitFormat" xml:space="preserve"> + <value>{0:f1}°F</value> + </data> + <data name="Menu_CustomRunners" xml:space="preserve"> + <value>커스텀 러너 설정...</value> + </data> + <data name="Window_CustomRunners" xml:space="preserve"> + <value>커스텀 러너</value> + </data> + <data name="CustomRunner_AddedRunners" xml:space="preserve"> + <value>추가된 러너 목록:</value> + </data> + <data name="CustomRunner_Name" xml:space="preserve"> + <value>러너 이름:</value> + </data> + <data name="CustomRunner_AddFrames" xml:space="preserve"> + <value>프레임 추가...</value> + </data> + <data name="CustomRunner_RemoveFrame" xml:space="preserve"> + <value>프레임 삭제</value> + </data> + <data name="CustomRunner_Save" xml:space="preserve"> + <value>저장</value> + </data> + <data name="CustomRunner_Delete" xml:space="preserve"> + <value>삭제</value> + </data> + <data name="CustomRunner_Requirements" xml:space="preserve"> + <value>포맷: PNG | 최대 크기: 32x32px</value> + </data> + <data name="CustomRunner_ConfirmOverwrite" xml:space="preserve"> + <value>같은 이름의 러너가 이미 존재합니다. 덮어쓰시겠습니까?</value> + </data> + <data name="CustomRunner_ConfirmDelete" xml:space="preserve"> + <value>"{0}" 러너를 삭제하시겠습니까?</value> + </data> + <data name="CustomRunner_RequirementsLabel" xml:space="preserve"> + <value>요구 사양:</value> + </data> + <data name="CustomRunner_FramesLabel" xml:space="preserve"> + <value>프레임:</value> + </data> + <data name="CustomRunner_Preview" xml:space="preserve"> + <value>미리보기:</value> + </data> + <data name="CustomRunner_PreviewSpeed" xml:space="preserve"> + <value>미리보기 속도:</value> + </data> +</root> \ No newline at end of file diff --git a/RunCat365/Properties/Strings.resx b/RunCat365/Properties/Strings.resx new file mode 100644 index 0000000..34aeace --- /dev/null +++ b/RunCat365/Properties/Strings.resx @@ -0,0 +1,303 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Menu_Runner" xml:space="preserve"> + <value>Runner</value> + </data> + <data name="Menu_Theme" xml:space="preserve"> + <value>Theme</value> + </data> + <data name="Menu_FPSMaxLimit" xml:space="preserve"> + <value>FPS Max Limit</value> + </data> + <data name="Menu_LaunchAtStartup" xml:space="preserve"> + <value>Launch at startup</value> + </data> + <data name="Menu_Settings" xml:space="preserve"> + <value>Settings</value> + </data> + <data name="Menu_EndlessGame" xml:space="preserve"> + <value>Endless Game</value> + </data> + <data name="Menu_Information" xml:space="preserve"> + <value>Information</value> + </data> + <data name="Menu_OpenProjectPage" xml:space="preserve"> + <value>Open Project Page</value> + </data> + <data name="Menu_Exit" xml:space="preserve"> + <value>Exit</value> + </data> + <data name="Theme_System" xml:space="preserve"> + <value>System</value> + </data> + <data name="Theme_Light" xml:space="preserve"> + <value>Light</value> + </data> + <data name="Theme_Dark" xml:space="preserve"> + <value>Dark</value> + </data> + <data name="Menu_TemperatureUnit" xml:space="preserve"> + <value>Temperature Unit</value> + </data> + <data name="TemperatureUnit_System" xml:space="preserve"> + <value>System</value> + </data> + <data name="TemperatureUnit_Celsius" xml:space="preserve"> + <value>Celsius</value> + </data> + <data name="TemperatureUnit_Fahrenheit" xml:space="preserve"> + <value>Fahrenheit</value> + </data> + <data name="Runner_Cat" xml:space="preserve"> + <value>Cat</value> + </data> + <data name="Runner_Parrot" xml:space="preserve"> + <value>Parrot</value> + </data> + <data name="Runner_Horse" xml:space="preserve"> + <value>Horse</value> + </data> + <data name="Message_AppLaunched" xml:space="preserve"> + <value>App has launched. If the icon is not on the taskbar, it has been omitted, so please move it manually and pin it.</value> + </data> + <data name="Message_Warning" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="Window_EndlessGame" xml:space="preserve"> + <value>Endless Game</value> + </data> + <data name="Game_PressSpaceToPlay" xml:space="preserve"> + <value>Press space to play.</value> + </data> + <data name="Game_GameOver" xml:space="preserve"> + <value>GAME OVER</value> + </data> + <data name="SystemInfo_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="SystemInfo_User" xml:space="preserve"> + <value>User</value> + </data> + <data name="SystemInfo_Kernel" xml:space="preserve"> + <value>Kernel</value> + </data> + <data name="SystemInfo_Available" xml:space="preserve"> + <value>Available</value> + </data> + <data name="SystemInfo_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="SystemInfo_Average" xml:space="preserve"> + <value>Average</value> + </data> + <data name="SystemInfo_Maximum" xml:space="preserve"> + <value>Maximum</value> + </data> + <data name="SystemInfo_Memory" xml:space="preserve"> + <value>Memory</value> + </data> + <data name="SystemInfo_Total" xml:space="preserve"> + <value>Total</value> + </data> + <data name="SystemInfo_Used" xml:space="preserve"> + <value>Used</value> + </data> + <data name="SystemInfo_Storage" xml:space="preserve"> + <value>Storage</value> + </data> + <data name="SystemInfo_DriveC" xml:space="preserve"> + <value>C Drive</value> + </data> + <data name="SystemInfo_DriveD" xml:space="preserve"> + <value>D Drive</value> + </data> + <data name="SystemInfo_Network" xml:space="preserve"> + <value>Network</value> + </data> + <data name="SystemInfo_Sent" xml:space="preserve"> + <value>Sent</value> + </data> + <data name="SystemInfo_Received" xml:space="preserve"> + <value>Received</value> + </data> + <data name="Message_CPUUsageUnavailable" xml:space="preserve"> + <value>Failed to get CPU usage. Unable to update running speed.</value> + </data> + <data name="Menu_SpeedSource" xml:space="preserve"> + <value>Speed based on</value> + </data> + <data name="Game_NewRecord" xml:space="preserve"> + <value>New Record</value> + </data> + <data name="SystemInfo_Temperature" xml:space="preserve"> + <value>Temperature</value> + </data> + <data name="SystemInfo_TemperatureCelsiusFormat" xml:space="preserve"> + <value>{0:f1}°C</value> + </data> + <data name="SystemInfo_TemperatureFahrenheitFormat" xml:space="preserve"> + <value>{0:f1}°F</value> + </data> + <data name="Menu_CustomRunners" xml:space="preserve"> + <value>Custom Runners...</value> + </data> + <data name="Window_CustomRunners" xml:space="preserve"> + <value>Custom Runners</value> + </data> + <data name="CustomRunner_AddedRunners" xml:space="preserve"> + <value>Custom Runners:</value> + </data> + <data name="CustomRunner_Name" xml:space="preserve"> + <value>Runner Name:</value> + </data> + <data name="CustomRunner_AddFrames" xml:space="preserve"> + <value>Add Frames...</value> + </data> + <data name="CustomRunner_RemoveFrame" xml:space="preserve"> + <value>Remove Frame</value> + </data> + <data name="CustomRunner_Save" xml:space="preserve"> + <value>Save</value> + </data> + <data name="CustomRunner_Delete" xml:space="preserve"> + <value>Delete</value> + </data> + <data name="CustomRunner_Requirements" xml:space="preserve"> + <value>Format: PNG | Max Dimensions: 32x32px</value> + </data> + <data name="CustomRunner_ConfirmOverwrite" xml:space="preserve"> + <value>A runner with this name already exists. Overwrite?</value> + </data> + <data name="CustomRunner_ConfirmDelete" xml:space="preserve"> + <value>Delete runner "{0}"?</value> + </data> + <data name="CustomRunner_RequirementsLabel" xml:space="preserve"> + <value>Requirements:</value> + </data> + <data name="CustomRunner_FramesLabel" xml:space="preserve"> + <value>Frames:</value> + </data> + <data name="CustomRunner_Preview" xml:space="preserve"> + <value>Preview:</value> + </data> + <data name="CustomRunner_PreviewSpeed" xml:space="preserve"> + <value>Preview Speed:</value> + </data> +</root> \ No newline at end of file diff --git a/RunCat365/Properties/Strings.zh-CN.resx b/RunCat365/Properties/Strings.zh-CN.resx new file mode 100644 index 0000000..b065ec5 --- /dev/null +++ b/RunCat365/Properties/Strings.zh-CN.resx @@ -0,0 +1,303 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Menu_Runner" xml:space="preserve"> + <value>跑步者</value> + </data> + <data name="Menu_Theme" xml:space="preserve"> + <value>主题</value> + </data> + <data name="Menu_FPSMaxLimit" xml:space="preserve"> + <value>FPS 最大限制</value> + </data> + <data name="Menu_LaunchAtStartup" xml:space="preserve"> + <value>跟随 Windows 启动</value> + </data> + <data name="Menu_Settings" xml:space="preserve"> + <value>设置</value> + </data> + <data name="Menu_EndlessGame" xml:space="preserve"> + <value>无尽游戏</value> + </data> + <data name="Menu_Information" xml:space="preserve"> + <value>信息</value> + </data> + <data name="Menu_OpenProjectPage" xml:space="preserve"> + <value>打开项目页面</value> + </data> + <data name="Menu_Exit" xml:space="preserve"> + <value>退出</value> + </data> + <data name="Theme_System" xml:space="preserve"> + <value>系统</value> + </data> + <data name="Theme_Light" xml:space="preserve"> + <value>浅色</value> + </data> + <data name="Theme_Dark" xml:space="preserve"> + <value>深色</value> + </data> + <data name="Menu_TemperatureUnit" xml:space="preserve"> + <value>温度单位</value> + </data> + <data name="TemperatureUnit_System" xml:space="preserve"> + <value>系统</value> + </data> + <data name="TemperatureUnit_Celsius" xml:space="preserve"> + <value>摄氏</value> + </data> + <data name="TemperatureUnit_Fahrenheit" xml:space="preserve"> + <value>华氏</value> + </data> + <data name="Runner_Cat" xml:space="preserve"> + <value>猫咪</value> + </data> + <data name="Runner_Parrot" xml:space="preserve"> + <value>鹦鹉</value> + </data> + <data name="Runner_Horse" xml:space="preserve"> + <value>马</value> + </data> + <data name="Message_AppLaunched" xml:space="preserve"> + <value>应用已启动。如果图标不在任务栏上,则表示该应用已被省略,请手动移动并将其固定。</value> + </data> + <data name="Message_Warning" xml:space="preserve"> + <value>警告</value> + </data> + <data name="Window_EndlessGame" xml:space="preserve"> + <value>无尽游戏</value> + </data> + <data name="Game_PressSpaceToPlay" xml:space="preserve"> + <value>按空格键开始。</value> + </data> + <data name="Game_GameOver" xml:space="preserve"> + <value>游戏结束</value> + </data> + <data name="SystemInfo_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="SystemInfo_User" xml:space="preserve"> + <value>用户</value> + </data> + <data name="SystemInfo_Kernel" xml:space="preserve"> + <value>内核</value> + </data> + <data name="SystemInfo_Available" xml:space="preserve"> + <value>可用</value> + </data> + <data name="SystemInfo_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="SystemInfo_Average" xml:space="preserve"> + <value>平均</value> + </data> + <data name="SystemInfo_Maximum" xml:space="preserve"> + <value>最大</value> + </data> + <data name="SystemInfo_Memory" xml:space="preserve"> + <value>内存</value> + </data> + <data name="SystemInfo_Total" xml:space="preserve"> + <value>总计</value> + </data> + <data name="SystemInfo_Used" xml:space="preserve"> + <value>已使用</value> + </data> + <data name="SystemInfo_Storage" xml:space="preserve"> + <value>存储</value> + </data> + <data name="SystemInfo_DriveC" xml:space="preserve"> + <value>C 盘</value> + </data> + <data name="SystemInfo_DriveD" xml:space="preserve"> + <value>D 盘</value> + </data> + <data name="SystemInfo_Network" xml:space="preserve"> + <value>网络</value> + </data> + <data name="SystemInfo_Sent" xml:space="preserve"> + <value>发送</value> + </data> + <data name="SystemInfo_Received" xml:space="preserve"> + <value>已接收</value> + </data> + <data name="Message_CPUUsageUnavailable" xml:space="preserve"> + <value>获取 CPU 使用率失败。无法更新运行速度。</value> + </data> + <data name="Menu_SpeedSource" xml:space="preserve"> + <value>速度来源</value> + </data> + <data name="Game_NewRecord" xml:space="preserve"> + <value>新纪录</value> + </data> + <data name="SystemInfo_Temperature" xml:space="preserve"> + <value>温度</value> + </data> + <data name="SystemInfo_TemperatureCelsiusFormat" xml:space="preserve"> + <value>{0:f1}°C</value> + </data> + <data name="SystemInfo_TemperatureFahrenheitFormat" xml:space="preserve"> + <value>{0:f1}°F</value> + </data> + <data name="Menu_CustomRunners" xml:space="preserve"> + <value>自定义跑步者...</value> + </data> + <data name="Window_CustomRunners" xml:space="preserve"> + <value>自定义跑步者</value> + </data> + <data name="CustomRunner_AddedRunners" xml:space="preserve"> + <value>自定义跑步者:</value> + </data> + <data name="CustomRunner_Name" xml:space="preserve"> + <value>跑步者名称:</value> + </data> + <data name="CustomRunner_AddFrames" xml:space="preserve"> + <value>添加帧...</value> + </data> + <data name="CustomRunner_RemoveFrame" xml:space="preserve"> + <value>删除帧</value> + </data> + <data name="CustomRunner_Save" xml:space="preserve"> + <value>保存</value> + </data> + <data name="CustomRunner_Delete" xml:space="preserve"> + <value>删除</value> + </data> + <data name="CustomRunner_Requirements" xml:space="preserve"> + <value>格式: PNG | 最大尺寸: 32x32px</value> + </data> + <data name="CustomRunner_ConfirmOverwrite" xml:space="preserve"> + <value>同名跑步者已存在。是否覆盖?</value> + </data> + <data name="CustomRunner_ConfirmDelete" xml:space="preserve"> + <value>删除跑步者"{0}"?</value> + </data> + <data name="CustomRunner_RequirementsLabel" xml:space="preserve"> + <value>要求:</value> + </data> + <data name="CustomRunner_FramesLabel" xml:space="preserve"> + <value>帧:</value> + </data> + <data name="CustomRunner_Preview" xml:space="preserve"> + <value>预览:</value> + </data> + <data name="CustomRunner_PreviewSpeed" xml:space="preserve"> + <value>预览速度:</value> + </data> +</root> \ No newline at end of file diff --git a/RunCat365/Properties/Strings.zh-TW.resx b/RunCat365/Properties/Strings.zh-TW.resx new file mode 100644 index 0000000..6e643a1 --- /dev/null +++ b/RunCat365/Properties/Strings.zh-TW.resx @@ -0,0 +1,303 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="Menu_Runner" xml:space="preserve"> + <value>跑步者</value> + </data> + <data name="Menu_Theme" xml:space="preserve"> + <value>主題</value> + </data> + <data name="Menu_FPSMaxLimit" xml:space="preserve"> + <value>FPS 最大限制</value> + </data> + <data name="Menu_LaunchAtStartup" xml:space="preserve"> + <value>跟隨 Windows 啟動</value> + </data> + <data name="Menu_Settings" xml:space="preserve"> + <value>設定</value> + </data> + <data name="Menu_EndlessGame" xml:space="preserve"> + <value>無盡遊戲</value> + </data> + <data name="Menu_Information" xml:space="preserve"> + <value>資訊</value> + </data> + <data name="Menu_OpenProjectPage" xml:space="preserve"> + <value>開啟專案頁面</value> + </data> + <data name="Menu_Exit" xml:space="preserve"> + <value>結束</value> + </data> + <data name="Theme_System" xml:space="preserve"> + <value>系統</value> + </data> + <data name="Theme_Light" xml:space="preserve"> + <value>淺色</value> + </data> + <data name="Theme_Dark" xml:space="preserve"> + <value>深色</value> + </data> + <data name="Menu_TemperatureUnit" xml:space="preserve"> + <value>溫度單位</value> + </data> + <data name="TemperatureUnit_System" xml:space="preserve"> + <value>系統</value> + </data> + <data name="TemperatureUnit_Celsius" xml:space="preserve"> + <value>攝氏</value> + </data> + <data name="TemperatureUnit_Fahrenheit" xml:space="preserve"> + <value>華氏</value> + </data> + <data name="Runner_Cat" xml:space="preserve"> + <value>貓咪</value> + </data> + <data name="Runner_Parrot" xml:space="preserve"> + <value>鸚鵡</value> + </data> + <data name="Runner_Horse" xml:space="preserve"> + <value>馬</value> + </data> + <data name="Message_AppLaunched" xml:space="preserve"> + <value>應用程式已啟動。如果圖示不在工作列上,表示已被省略,請手動移動並固定它。</value> + </data> + <data name="Message_Warning" xml:space="preserve"> + <value>警告</value> + </data> + <data name="Window_EndlessGame" xml:space="preserve"> + <value>無盡遊戲</value> + </data> + <data name="Game_PressSpaceToPlay" xml:space="preserve"> + <value>按空白鍵開始。</value> + </data> + <data name="Game_GameOver" xml:space="preserve"> + <value>遊戲結束</value> + </data> + <data name="SystemInfo_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="SystemInfo_User" xml:space="preserve"> + <value>使用者</value> + </data> + <data name="SystemInfo_Kernel" xml:space="preserve"> + <value>核心</value> + </data> + <data name="SystemInfo_Available" xml:space="preserve"> + <value>可用</value> + </data> + <data name="SystemInfo_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="SystemInfo_Average" xml:space="preserve"> + <value>平均</value> + </data> + <data name="SystemInfo_Maximum" xml:space="preserve"> + <value>最大</value> + </data> + <data name="SystemInfo_Memory" xml:space="preserve"> + <value>記憶體</value> + </data> + <data name="SystemInfo_Total" xml:space="preserve"> + <value>總計</value> + </data> + <data name="SystemInfo_Used" xml:space="preserve"> + <value>已使用</value> + </data> + <data name="SystemInfo_Storage" xml:space="preserve"> + <value>儲存</value> + </data> + <data name="SystemInfo_DriveC" xml:space="preserve"> + <value>C 槽</value> + </data> + <data name="SystemInfo_DriveD" xml:space="preserve"> + <value>D 槽</value> + </data> + <data name="SystemInfo_Network" xml:space="preserve"> + <value>網路</value> + </data> + <data name="SystemInfo_Sent" xml:space="preserve"> + <value>傳送</value> + </data> + <data name="SystemInfo_Received" xml:space="preserve"> + <value>已接收</value> + </data> + <data name="Message_CPUUsageUnavailable" xml:space="preserve"> + <value>取得 CPU 使用率失敗。無法更新執行速度。</value> + </data> + <data name="Menu_SpeedSource" xml:space="preserve"> + <value>速度來源</value> + </data> + <data name="Game_NewRecord" xml:space="preserve"> + <value>新紀錄</value> + </data> + <data name="SystemInfo_Temperature" xml:space="preserve"> + <value>溫度</value> + </data> + <data name="SystemInfo_TemperatureCelsiusFormat" xml:space="preserve"> + <value>{0:f1}°C</value> + </data> + <data name="SystemInfo_TemperatureFahrenheitFormat" xml:space="preserve"> + <value>{0:f1}°F</value> + </data> + <data name="Menu_CustomRunners" xml:space="preserve"> + <value>自訂跑步者...</value> + </data> + <data name="Window_CustomRunners" xml:space="preserve"> + <value>自訂跑步者</value> + </data> + <data name="CustomRunner_AddedRunners" xml:space="preserve"> + <value>自訂跑步者:</value> + </data> + <data name="CustomRunner_Name" xml:space="preserve"> + <value>跑步者名稱:</value> + </data> + <data name="CustomRunner_AddFrames" xml:space="preserve"> + <value>新增影格...</value> + </data> + <data name="CustomRunner_RemoveFrame" xml:space="preserve"> + <value>刪除影格</value> + </data> + <data name="CustomRunner_Save" xml:space="preserve"> + <value>儲存</value> + </data> + <data name="CustomRunner_Delete" xml:space="preserve"> + <value>刪除</value> + </data> + <data name="CustomRunner_Requirements" xml:space="preserve"> + <value>格式: PNG | 最大尺寸: 32x32px</value> + </data> + <data name="CustomRunner_ConfirmOverwrite" xml:space="preserve"> + <value>同名跑步者已存在。是否覆蓋?</value> + </data> + <data name="CustomRunner_ConfirmDelete" xml:space="preserve"> + <value>刪除跑步者「{0}」?</value> + </data> + <data name="CustomRunner_RequirementsLabel" xml:space="preserve"> + <value>需求:</value> + </data> + <data name="CustomRunner_FramesLabel" xml:space="preserve"> + <value>影格:</value> + </data> + <data name="CustomRunner_Preview" xml:space="preserve"> + <value>預覽:</value> + </data> + <data name="CustomRunner_PreviewSpeed" xml:space="preserve"> + <value>預覽速度:</value> + </data> +</root> \ No newline at end of file diff --git a/RunCat365/Properties/UserSettings.Designer.cs b/RunCat365/Properties/UserSettings.Designer.cs new file mode 100644 index 0000000..c22b676 --- /dev/null +++ b/RunCat365/Properties/UserSettings.Designer.cs @@ -0,0 +1,122 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace RunCat365.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.14.0.0")] + internal sealed partial class UserSettings : global::System.Configuration.ApplicationSettingsBase { + + private static UserSettings defaultInstance = ((UserSettings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new UserSettings()))); + + public static UserSettings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Cat")] + public string Runner { + get { + return ((string)(this["Runner"])); + } + set { + this["Runner"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Theme { + get { + return ((string)(this["Theme"])); + } + set { + this["Theme"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string TemperatureUnit { + get { + return ((string)(this["TemperatureUnit"])); + } + set { + this["TemperatureUnit"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("FPS40")] + public string FPSMaxLimit { + get { + return ((string)(this["FPSMaxLimit"])); + } + set { + this["FPSMaxLimit"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool FirstLaunch { + get { + return ((bool)(this["FirstLaunch"])); + } + set { + this["FirstLaunch"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int HighScore { + get { + return ((int)(this["HighScore"])); + } + set { + this["HighScore"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("CPU")] + public string SpeedSource { + get { + return ((string)(this["SpeedSource"])); + } + set { + this["SpeedSource"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string CustomRunnerName { + get { + return ((string)(this["CustomRunnerName"])); + } + set { + this["CustomRunnerName"] = value; + } + } + } +} diff --git a/RunCat365/Properties/UserSettings.settings b/RunCat365/Properties/UserSettings.settings new file mode 100644 index 0000000..57c0c84 --- /dev/null +++ b/RunCat365/Properties/UserSettings.settings @@ -0,0 +1,30 @@ +<?xml version='1.0' encoding='utf-8'?> +<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="RunCat365.Properties" GeneratedClassName="UserSettings"> + <Profiles /> + <Settings> + <Setting Name="Runner" Type="System.String" Scope="User"> + <Value Profile="(Default)">Cat</Value> + </Setting> + <Setting Name="Theme" Type="System.String" Scope="User"> + <Value Profile="(Default)" /> + </Setting> + <Setting Name="TemperatureUnit" Type="System.String" Scope="User"> + <Value Profile="(Default)" /> + </Setting> + <Setting Name="FPSMaxLimit" Type="System.String" Scope="User"> + <Value Profile="(Default)">FPS40</Value> + </Setting> + <Setting Name="FirstLaunch" Type="System.Boolean" Scope="User"> + <Value Profile="(Default)">True</Value> + </Setting> + <Setting Name="HighScore" Type="System.Int32" Scope="User"> + <Value Profile="(Default)">0</Value> + </Setting> + <Setting Name="SpeedSource" Type="System.String" Scope="User"> + <Value Profile="(Default)">CPU</Value> + </Setting> + <Setting Name="CustomRunnerName" Type="System.String" Scope="User"> + <Value Profile="(Default)" /> + </Setting> + </Settings> +</SettingsFile> \ No newline at end of file diff --git a/RunCat365/Road.cs b/RunCat365/Road.cs new file mode 100644 index 0000000..6c3e38c --- /dev/null +++ b/RunCat365/Road.cs @@ -0,0 +1,39 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace RunCat365 +{ + internal enum Road + { + Flat, + Hill, + Crater, + Sprout + } + + internal static class RoadExtension + { + internal static string GetString(this Road road) + { + return road switch + { + Road.Flat => "Flat", + Road.Hill => "Hill", + Road.Crater => "Crater", + Road.Sprout => "Sprout", + _ => "", + }; + } + } +} diff --git a/RunCat365/RunCat365.csproj b/RunCat365/RunCat365.csproj new file mode 100644 index 0000000..660a9c5 --- /dev/null +++ b/RunCat365/RunCat365.csproj @@ -0,0 +1,77 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <OutputType>WinExe</OutputType> + <TargetFramework>net9.0-windows10.0.26100.0</TargetFramework> + <Nullable>enable</Nullable> + <UseWindowsForms>true</UseWindowsForms> + <ImplicitUsings>enable</ImplicitUsings> + <SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion> + <AssemblyName>RunCat 365</AssemblyName> + <ApplicationIcon>resources\app_icon.ico</ApplicationIcon> + <Authors>Takuto Nakamura</Authors> + <Company>Studio Kyome</Company> + <Description>A cute running cat animation on your taskbar.</Description> + <Copyright>© 2022 Takuto Nakamura</Copyright> + <PackageProjectUrl>https://runcat-dev.github.io/RunCat365/</PackageProjectUrl> + <RepositoryUrl>https://github.com/runcat-dev/RunCat365</RepositoryUrl> + <PackageTags>cpu;cat;taskbar</PackageTags> + <PackageLicenseFile>LICENSE</PackageLicenseFile> + <PackageIcon>app_icon.png</PackageIcon> + <Version>3.6.0</Version> + <PackageReadmeFile>README.md</PackageReadmeFile> + <StartupObject>RunCat365.Program</StartupObject> + <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> + <Platforms>x64;x86;ARM64</Platforms> + <ApplicationManifest>App.manifest</ApplicationManifest> + <NoWarn>$(NoWarn);WFO5001</NoWarn> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + + <ItemGroup> + <Content Include="resources\app_icon.ico" /> + </ItemGroup> + + <ItemGroup> + <None Include="..\docs\images\app_icon.png"> + <Pack>True</Pack> + <PackagePath>\</PackagePath> + </None> + <None Include="..\LICENSE"> + <Pack>True</Pack> + <PackagePath>\</PackagePath> + </None> + <None Include="..\README.md"> + <Pack>True</Pack> + <PackagePath>\</PackagePath> + </None> + </ItemGroup> + + <ItemGroup> + <Compile Update="Properties\Resources.Designer.cs"> + <DesignTime>True</DesignTime> + <AutoGen>True</AutoGen> + <DependentUpon>Resources.resx</DependentUpon> + </Compile> + <Compile Update="Properties\UserSettings.Designer.cs"> + <DesignTimeSharedInput>True</DesignTimeSharedInput> + <AutoGen>True</AutoGen> + <DependentUpon>UserSettings.settings</DependentUpon> + </Compile> + </ItemGroup> + + <ItemGroup> + <EmbeddedResource Update="Properties\Resources.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>Resources.Designer.cs</LastGenOutput> + </EmbeddedResource> + </ItemGroup> + + <ItemGroup> + <None Update="Properties\UserSettings.settings"> + <Generator>SettingsSingleFileGenerator</Generator> + <LastGenOutput>UserSettings.Designer.cs</LastGenOutput> + </None> + </ItemGroup> + +</Project> diff --git a/RunCat365/Runner.cs b/RunCat365/Runner.cs new file mode 100644 index 0000000..4da2d23 --- /dev/null +++ b/RunCat365/Runner.cs @@ -0,0 +1,61 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; + +namespace RunCat365 +{ + enum Runner + { + Cat, + Parrot, + Horse, + } + + internal static class RunnerExtension + { + internal static string GetString(this Runner runner) + { + return runner switch + { + Runner.Cat => "Cat", + Runner.Parrot => "Parrot", + Runner.Horse => "Horse", + _ => "", + }; + } + + internal static string GetLocalizedString(this Runner runner) + { + return runner switch + { + Runner.Cat => Strings.Runner_Cat, + Runner.Parrot => Strings.Runner_Parrot, + Runner.Horse => Strings.Runner_Horse, + _ => "", + }; + } + + internal static int GetFrameNumber(this Runner runner) + { + return runner switch + { + Runner.Cat => 5, + Runner.Parrot => 10, + Runner.Horse => 14, + _ => 0, + }; + } + } +} diff --git a/RunCat365/SpeedSource.cs b/RunCat365/SpeedSource.cs new file mode 100644 index 0000000..a5bc2c1 --- /dev/null +++ b/RunCat365/SpeedSource.cs @@ -0,0 +1,62 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; +using System.Diagnostics.CodeAnalysis; + +namespace RunCat365 +{ + enum SpeedSource + { + CPU, + GPU, + Memory, + } + + internal static class SpeedSourceExtension + { + internal static string GetLocalizedString(this SpeedSource speedSource) + { + return speedSource switch + { + SpeedSource.CPU => Strings.SystemInfo_CPU, + SpeedSource.GPU => Strings.SystemInfo_GPU, + SpeedSource.Memory => Strings.SystemInfo_Memory, + _ => "", + }; + } + + internal static bool TryParse([NotNullWhen(true)] string? value, out SpeedSource result) + { + SpeedSource? nullableResult = value switch + { + "CPU" => SpeedSource.CPU, + "GPU" => SpeedSource.GPU, + "Memory" => SpeedSource.Memory, + _ => null, + }; + + if (nullableResult is SpeedSource nonNullableResult) + { + result = nonNullableResult; + return true; + } + else + { + result = SpeedSource.CPU; + return false; + } + } + } +} diff --git a/RunCat365/StorageRepository.cs b/RunCat365/StorageRepository.cs new file mode 100644 index 0000000..f0b9632 --- /dev/null +++ b/RunCat365/StorageRepository.cs @@ -0,0 +1,129 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; + +namespace RunCat365 +{ + enum Drive + { + C, + D + } + + internal static class DriveExtension + { + internal static string GetString(this Drive drive) + { + return drive switch + { + Drive.C => "C Drive", + Drive.D => "D Drive", + _ => "", + }; + } + + internal static string GetLocalizedString(this Drive drive) + { + return drive switch + { + Drive.C => Strings.SystemInfo_DriveC, + Drive.D => Strings.SystemInfo_DriveD, + _ => "", + }; + } + + internal static Drive? CreateFromString(string? value) + { + return value switch + { + "C:\\" => Drive.C, + "D:\\" => Drive.D, + _ => null, + }; + } + } + + struct StorageInfo + { + internal Drive Drive { get; set; } + internal long TotalSize { get; set; } + internal long AvailableSpaceSize { get; set; } + internal long UsedSpaceSize { get; set; } + } + + internal static class StorageInfoExtension + { + internal static List<string> GenerateIndicator(this List<StorageInfo> storageInfoList) + { + var resultLines = new List<string> + { + TreeFormatter.CreateRoot($"{Strings.SystemInfo_Storage}:") + }; + + if (storageInfoList.Count == 0) return resultLines; + + for (int i = 0; i < storageInfoList.Count; i++) + { + var info = storageInfoList[i]; + var isLastItem = (i == storageInfoList.Count - 1); + var percentage = ((double)info.UsedSpaceSize / info.TotalSize) * 100.0; + resultLines.Add(TreeFormatter.CreateNode($"{info.Drive.GetLocalizedString()}: {percentage:f1}%", isLastItem)); + resultLines.Add(TreeFormatter.CreateNestedNode($"{Strings.SystemInfo_Used}: {info.UsedSpaceSize.ToByteFormatted()}", isLastItem, false)); + resultLines.Add(TreeFormatter.CreateNestedNode($"{Strings.SystemInfo_Available}: {info.AvailableSpaceSize.ToByteFormatted()}", isLastItem, true)); + } + + return resultLines; + } + } + + internal class StorageRepository + { + private readonly List<StorageInfo> storageInfoList = []; + + internal StorageRepository() { } + + internal void Update() + { + storageInfoList.Clear(); + var allDrives = DriveInfo.GetDrives(); + foreach (var driveInfo in allDrives) + { + if (driveInfo.IsReady && DriveExtension.CreateFromString(driveInfo.Name) is Drive drive) + { + try + { + var storageInfo = new StorageInfo + { + Drive = drive, + TotalSize = driveInfo.TotalSize, + AvailableSpaceSize = driveInfo.AvailableFreeSpace, + UsedSpaceSize = driveInfo.TotalSize - driveInfo.AvailableFreeSpace + }; + storageInfoList.Add(storageInfo); + } + catch (Exception ex) + { + Console.WriteLine($"Error: {ex.Message}"); + } + } + } + } + + internal List<StorageInfo> Get() + { + return storageInfoList; + } + } +} diff --git a/RunCat365/SupportedLanguage.cs b/RunCat365/SupportedLanguage.cs new file mode 100644 index 0000000..1189dc9 --- /dev/null +++ b/RunCat365/SupportedLanguage.cs @@ -0,0 +1,100 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Globalization; + +namespace RunCat365 +{ + enum SupportedLanguage + { + ChineseSimplified, + ChineseTraditional, + English, + French, + German, + Japanese, + Korean, + Spanish, + } + + internal static class SupportedLanguageExtension + { + private static SupportedLanguage DetectChineseVariant(CultureInfo culture) + { + return culture.Name.Contains("Hant") || culture.Name is "zh-TW" or "zh-HK" or "zh-MO" + ? SupportedLanguage.ChineseTraditional + : SupportedLanguage.ChineseSimplified; + } + + internal static SupportedLanguage GetCurrentLanguage() + { + var culture = CultureInfo.CurrentUICulture; + return culture.TwoLetterISOLanguageName switch + { + "zh" => DetectChineseVariant(culture), + "fr" => SupportedLanguage.French, + "de" => SupportedLanguage.German, + "ja" => SupportedLanguage.Japanese, + "ko" => SupportedLanguage.Korean, + "es" => SupportedLanguage.Spanish, + _ => SupportedLanguage.English, + }; + } + + internal static CultureInfo GetDefaultCultureInfo(this SupportedLanguage language) + { + return language switch + { + SupportedLanguage.ChineseSimplified => new CultureInfo("zh-CN"), + SupportedLanguage.ChineseTraditional => new CultureInfo("zh-TW"), + SupportedLanguage.French => new CultureInfo("fr-FR"), + SupportedLanguage.German => new CultureInfo("de-DE"), + SupportedLanguage.Japanese => new CultureInfo("ja-JP"), + SupportedLanguage.Korean => new CultureInfo("ko-KR"), + SupportedLanguage.Spanish => new CultureInfo("es-ES"), + _ => new CultureInfo("en-US"), + }; + } + + internal static string GetFontName(this SupportedLanguage language) + { + return language switch + { + SupportedLanguage.ChineseSimplified => "Microsoft YaHei", + SupportedLanguage.ChineseTraditional => "Microsoft JhengHei", + SupportedLanguage.French => "Consolas", + SupportedLanguage.German => "Consolas", + SupportedLanguage.Japanese => "Noto Sans JP", + SupportedLanguage.Korean => "Malgun Gothic", + SupportedLanguage.Spanish => "Consolas", + _ => "Consolas", + }; + } + + internal static bool IsFullWidth(this SupportedLanguage language) + { + return language switch + { + SupportedLanguage.ChineseSimplified => true, + SupportedLanguage.ChineseTraditional => true, + SupportedLanguage.French => false, + SupportedLanguage.German => false, + SupportedLanguage.Japanese => true, + SupportedLanguage.Korean => true, + SupportedLanguage.Spanish => false, + _ => false, + }; + } + } +} diff --git a/RunCat365/TemperatureRepository.cs b/RunCat365/TemperatureRepository.cs new file mode 100644 index 0000000..ecbe1a0 --- /dev/null +++ b/RunCat365/TemperatureRepository.cs @@ -0,0 +1,130 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; +using System.Globalization; + +namespace RunCat365 +{ + struct TemperatureInfo + { + internal float AverageCelsius { get; set; } + internal float MaximumCelsius { get; set; } + } + + internal static class TemperatureInfoExtension + { + private const float CELSIUS_TO_FAHRENHEIT_SCALE = 9.0f / 5.0f; + private const float CELSIUS_TO_FAHRENHEIT_OFFSET = 32.0f; + + internal static string GetDescription(this TemperatureInfo temperatureInfo, TemperatureUnit unit) + { + var resolvedUnit = unit.Resolve(); + return $"{Strings.SystemInfo_Temperature}: {temperatureInfo.MaximumCelsius.ToLocalizedTemperatureText(resolvedUnit)}"; + } + + internal static List<string> GenerateIndicator(this TemperatureInfo temperatureInfo, TemperatureUnit unit) + { + var resolvedUnit = unit.Resolve(); + return [ + TreeFormatter.CreateRoot($"{Strings.SystemInfo_Temperature}:"), + TreeFormatter.CreateNode($"{Strings.SystemInfo_Average}: {temperatureInfo.AverageCelsius.ToLocalizedTemperatureText(resolvedUnit)}", false), + TreeFormatter.CreateNode($"{Strings.SystemInfo_Maximum}: {temperatureInfo.MaximumCelsius.ToLocalizedTemperatureText(resolvedUnit)}", true) + ]; + } + + private static string ToLocalizedTemperatureText(this float temperatureCelsius, TemperatureUnit resolvedUnit) + { + var useFahrenheit = resolvedUnit == TemperatureUnit.Fahrenheit; + var value = useFahrenheit + ? temperatureCelsius * CELSIUS_TO_FAHRENHEIT_SCALE + CELSIUS_TO_FAHRENHEIT_OFFSET + : temperatureCelsius; + var format = useFahrenheit + ? Strings.SystemInfo_TemperatureFahrenheitFormat + : Strings.SystemInfo_TemperatureCelsiusFormat; + return string.Format(CultureInfo.CurrentCulture, format, value); + } + } + + internal sealed class TemperaturePerformanceCounters : InstancedPerformanceCounters + { + protected override string CategoryName => "Thermal Zone Information"; + protected override string CounterName => "Temperature"; + + internal static TemperaturePerformanceCounters? TryCreate() + { + var instance = new TemperaturePerformanceCounters(); + return instance.TryInitialize() ? instance : null; + } + } + + internal class TemperatureRepository + { + private const float KELVIN_TO_CELSIUS_OFFSET = 273.15f; + private const float MIN_VALID_TEMPERATURE_CELSIUS = -50.0f; + private const float MAX_VALID_TEMPERATURE_CELSIUS = 150.0f; + private const int REFRESH_INTERVAL_TICKS = 30; + + private readonly TemperaturePerformanceCounters? counters; + private TemperatureInfo? temperatureInfo; + private int ticksSinceLastRefresh; + + internal bool IsAvailable => counters is not null; + + internal TemperatureRepository() + { + counters = TemperaturePerformanceCounters.TryCreate(); + } + + internal void Update() + { + if (counters is null) return; + + ticksSinceLastRefresh += 1; + if (REFRESH_INTERVAL_TICKS <= ticksSinceLastRefresh) + { + ticksSinceLastRefresh = 0; + counters.RefreshInstances(); + } + + var rawValues = counters.ReadValues(); + var temperaturesCelsius = new List<float>(rawValues.Count); + foreach (var temperatureKelvin in rawValues) + { + if (temperatureKelvin <= 0) continue; + var temperatureCelsius = temperatureKelvin - KELVIN_TO_CELSIUS_OFFSET; + if (temperatureCelsius is < MIN_VALID_TEMPERATURE_CELSIUS or > MAX_VALID_TEMPERATURE_CELSIUS) continue; + temperaturesCelsius.Add(temperatureCelsius); + } + + temperatureInfo = temperaturesCelsius.Count == 0 + ? null + : new TemperatureInfo + { + AverageCelsius = temperaturesCelsius.Average(), + MaximumCelsius = temperaturesCelsius.Max() + }; + } + + internal TemperatureInfo? Get() + { + return temperatureInfo; + } + + internal void Close() + { + counters?.Close(); + } + } +} diff --git a/RunCat365/TemperatureUnit.cs b/RunCat365/TemperatureUnit.cs new file mode 100644 index 0000000..9f11863 --- /dev/null +++ b/RunCat365/TemperatureUnit.cs @@ -0,0 +1,61 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; +using System.Globalization; + +namespace RunCat365 +{ + enum TemperatureUnit + { + System, + Celsius, + Fahrenheit, + } + + internal static class TemperatureUnitExtension + { + private static readonly TemperatureUnit systemDefault = DetectSystemDefault(); + + internal static string GetLocalizedString(this TemperatureUnit unit) + { + return unit switch + { + TemperatureUnit.System => Strings.TemperatureUnit_System, + TemperatureUnit.Celsius => $"{Strings.TemperatureUnit_Celsius} (°C)", + TemperatureUnit.Fahrenheit => $"{Strings.TemperatureUnit_Fahrenheit} (°F)", + _ => "", + }; + } + + internal static TemperatureUnit Resolve(this TemperatureUnit unit) + { + return unit == TemperatureUnit.System ? systemDefault : unit; + } + + private static TemperatureUnit DetectSystemDefault() + { + try + { + return new RegionInfo(CultureInfo.CurrentCulture.Name).IsMetric + ? TemperatureUnit.Celsius + : TemperatureUnit.Fahrenheit; + } + catch (ArgumentException) + { + return TemperatureUnit.Celsius; + } + } + } +} diff --git a/RunCat365/Theme.cs b/RunCat365/Theme.cs new file mode 100644 index 0000000..05fca6e --- /dev/null +++ b/RunCat365/Theme.cs @@ -0,0 +1,49 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using RunCat365.Properties; + +namespace RunCat365 +{ + enum Theme + { + System, + Light, + Dark, + } + + internal static class ThemeExtension + { + internal static string GetLocalizedString(this Theme theme) + { + return theme switch + { + Theme.System => Strings.Theme_System, + Theme.Light => Strings.Theme_Light, + Theme.Dark => Strings.Theme_Dark, + _ => "", + }; + } + + internal static Color GetContrastColor(this Theme theme) + { + return theme switch + { + Theme.Dark => Color.White, + Theme.Light => Color.Black, + _ => Color.Gray, + }; + } + } +} diff --git a/RunCat365/TreeFormatter.cs b/RunCat365/TreeFormatter.cs new file mode 100644 index 0000000..c070462 --- /dev/null +++ b/RunCat365/TreeFormatter.cs @@ -0,0 +1,44 @@ +// Copyright 2025 Takuto Nakamura +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace RunCat365 +{ + internal static class TreeFormatter + { + private static readonly bool isFullWidth = SupportedLanguageExtension.GetCurrentLanguage().IsFullWidth(); + + private static string BranchMiddle => isFullWidth ? "├─" : "├─ "; + private static string BranchLast => isFullWidth ? "└─" : "└─ "; + private static string IndentContinue => isFullWidth ? "│ " : "│ "; + private static string IndentLast => isFullWidth ? "  " : " "; + + internal static string CreateRoot(string content) + { + return content; + } + + internal static string CreateNode(string content, bool isLast) + { + var prefix = isLast ? BranchLast : BranchMiddle; + return $"{prefix}{content}"; + } + + internal static string CreateNestedNode(string content, bool parentIsLast, bool isLast) + { + var indent = parentIsLast ? IndentLast : IndentContinue; + var prefix = isLast ? BranchLast : BranchMiddle; + return $"{indent}{prefix}{content}"; + } + } +} diff --git a/RunCat365/resources/app_icon.ico b/RunCat365/resources/app_icon.ico new file mode 100644 index 0000000..000e92c Binary files /dev/null and b/RunCat365/resources/app_icon.ico differ diff --git a/RunCat365/resources/game/cat_jumping_0.png b/RunCat365/resources/game/cat_jumping_0.png new file mode 100644 index 0000000..d84a575 Binary files /dev/null and b/RunCat365/resources/game/cat_jumping_0.png differ diff --git a/RunCat365/resources/game/cat_jumping_1.png b/RunCat365/resources/game/cat_jumping_1.png new file mode 100644 index 0000000..bf6bef4 Binary files /dev/null and b/RunCat365/resources/game/cat_jumping_1.png differ diff --git a/RunCat365/resources/game/cat_jumping_2.png b/RunCat365/resources/game/cat_jumping_2.png new file mode 100644 index 0000000..1d24b03 Binary files /dev/null and b/RunCat365/resources/game/cat_jumping_2.png differ diff --git a/RunCat365/resources/game/cat_jumping_3.png b/RunCat365/resources/game/cat_jumping_3.png new file mode 100644 index 0000000..6b0a977 Binary files /dev/null and b/RunCat365/resources/game/cat_jumping_3.png differ diff --git a/RunCat365/resources/game/cat_jumping_4.png b/RunCat365/resources/game/cat_jumping_4.png new file mode 100644 index 0000000..262afa5 Binary files /dev/null and b/RunCat365/resources/game/cat_jumping_4.png differ diff --git a/RunCat365/resources/game/cat_jumping_5.png b/RunCat365/resources/game/cat_jumping_5.png new file mode 100644 index 0000000..aa300df Binary files /dev/null and b/RunCat365/resources/game/cat_jumping_5.png differ diff --git a/RunCat365/resources/game/cat_jumping_6.png b/RunCat365/resources/game/cat_jumping_6.png new file mode 100644 index 0000000..08d577a Binary files /dev/null and b/RunCat365/resources/game/cat_jumping_6.png differ diff --git a/RunCat365/resources/game/cat_jumping_7.png b/RunCat365/resources/game/cat_jumping_7.png new file mode 100644 index 0000000..5dd8f31 Binary files /dev/null and b/RunCat365/resources/game/cat_jumping_7.png differ diff --git a/RunCat365/resources/game/cat_jumping_8.png b/RunCat365/resources/game/cat_jumping_8.png new file mode 100644 index 0000000..d9fdf82 Binary files /dev/null and b/RunCat365/resources/game/cat_jumping_8.png differ diff --git a/RunCat365/resources/game/cat_jumping_9.png b/RunCat365/resources/game/cat_jumping_9.png new file mode 100644 index 0000000..5632c45 Binary files /dev/null and b/RunCat365/resources/game/cat_jumping_9.png differ diff --git a/RunCat365/resources/game/cat_running_0.png b/RunCat365/resources/game/cat_running_0.png new file mode 100644 index 0000000..2781331 Binary files /dev/null and b/RunCat365/resources/game/cat_running_0.png differ diff --git a/RunCat365/resources/game/cat_running_1.png b/RunCat365/resources/game/cat_running_1.png new file mode 100644 index 0000000..c7ac9a0 Binary files /dev/null and b/RunCat365/resources/game/cat_running_1.png differ diff --git a/RunCat365/resources/game/cat_running_2.png b/RunCat365/resources/game/cat_running_2.png new file mode 100644 index 0000000..2df844d Binary files /dev/null and b/RunCat365/resources/game/cat_running_2.png differ diff --git a/RunCat365/resources/game/cat_running_3.png b/RunCat365/resources/game/cat_running_3.png new file mode 100644 index 0000000..d145bf3 Binary files /dev/null and b/RunCat365/resources/game/cat_running_3.png differ diff --git a/RunCat365/resources/game/cat_running_4.png b/RunCat365/resources/game/cat_running_4.png new file mode 100644 index 0000000..d8937e9 Binary files /dev/null and b/RunCat365/resources/game/cat_running_4.png differ diff --git a/RunCat365/resources/game/road_crater.png b/RunCat365/resources/game/road_crater.png new file mode 100644 index 0000000..2d25a23 Binary files /dev/null and b/RunCat365/resources/game/road_crater.png differ diff --git a/RunCat365/resources/game/road_flat.png b/RunCat365/resources/game/road_flat.png new file mode 100644 index 0000000..ec74578 Binary files /dev/null and b/RunCat365/resources/game/road_flat.png differ diff --git a/RunCat365/resources/game/road_hill.png b/RunCat365/resources/game/road_hill.png new file mode 100644 index 0000000..c8f105d Binary files /dev/null and b/RunCat365/resources/game/road_hill.png differ diff --git a/RunCat365/resources/game/road_sprout.png b/RunCat365/resources/game/road_sprout.png new file mode 100644 index 0000000..93d3059 Binary files /dev/null and b/RunCat365/resources/game/road_sprout.png differ diff --git a/RunCat365/resources/rabbit.png b/RunCat365/resources/rabbit.png new file mode 100644 index 0000000..83e25af Binary files /dev/null and b/RunCat365/resources/rabbit.png differ diff --git a/RunCat365/resources/runners/cat/cat_0.png b/RunCat365/resources/runners/cat/cat_0.png new file mode 100644 index 0000000..c94ffeb Binary files /dev/null and b/RunCat365/resources/runners/cat/cat_0.png differ diff --git a/RunCat365/resources/runners/cat/cat_1.png b/RunCat365/resources/runners/cat/cat_1.png new file mode 100644 index 0000000..3bb178f Binary files /dev/null and b/RunCat365/resources/runners/cat/cat_1.png differ diff --git a/RunCat365/resources/runners/cat/cat_2.png b/RunCat365/resources/runners/cat/cat_2.png new file mode 100644 index 0000000..fd48ca8 Binary files /dev/null and b/RunCat365/resources/runners/cat/cat_2.png differ diff --git a/RunCat365/resources/runners/cat/cat_3.png b/RunCat365/resources/runners/cat/cat_3.png new file mode 100644 index 0000000..128fb5d Binary files /dev/null and b/RunCat365/resources/runners/cat/cat_3.png differ diff --git a/RunCat365/resources/runners/cat/cat_4.png b/RunCat365/resources/runners/cat/cat_4.png new file mode 100644 index 0000000..cccc51b Binary files /dev/null and b/RunCat365/resources/runners/cat/cat_4.png differ diff --git a/RunCat365/resources/runners/horse/horse_0.png b/RunCat365/resources/runners/horse/horse_0.png new file mode 100644 index 0000000..58f5ba2 Binary files /dev/null and b/RunCat365/resources/runners/horse/horse_0.png differ diff --git a/RunCat365/resources/runners/horse/horse_1.png b/RunCat365/resources/runners/horse/horse_1.png new file mode 100644 index 0000000..fb418be Binary files /dev/null and b/RunCat365/resources/runners/horse/horse_1.png differ diff --git a/RunCat365/resources/runners/horse/horse_2.png b/RunCat365/resources/runners/horse/horse_2.png new file mode 100644 index 0000000..335df4b Binary files /dev/null and b/RunCat365/resources/runners/horse/horse_2.png differ diff --git a/RunCat365/resources/runners/horse/horse_3.png b/RunCat365/resources/runners/horse/horse_3.png new file mode 100644 index 0000000..341718e Binary files /dev/null and b/RunCat365/resources/runners/horse/horse_3.png differ diff --git a/RunCat365/resources/runners/horse/horse_4.png b/RunCat365/resources/runners/horse/horse_4.png new file mode 100644 index 0000000..25423fb Binary files /dev/null and b/RunCat365/resources/runners/horse/horse_4.png differ diff --git a/RunCat365/resources/runners/parrot/parrot_0.png b/RunCat365/resources/runners/parrot/parrot_0.png new file mode 100644 index 0000000..a3e36f0 Binary files /dev/null and b/RunCat365/resources/runners/parrot/parrot_0.png differ diff --git a/RunCat365/resources/runners/parrot/parrot_1.png b/RunCat365/resources/runners/parrot/parrot_1.png new file mode 100644 index 0000000..9c1bb47 Binary files /dev/null and b/RunCat365/resources/runners/parrot/parrot_1.png differ diff --git a/RunCat365/resources/runners/parrot/parrot_2.png b/RunCat365/resources/runners/parrot/parrot_2.png new file mode 100644 index 0000000..9b6f3e0 Binary files /dev/null and b/RunCat365/resources/runners/parrot/parrot_2.png differ diff --git a/RunCat365/resources/runners/parrot/parrot_3.png b/RunCat365/resources/runners/parrot/parrot_3.png new file mode 100644 index 0000000..e2b01dd Binary files /dev/null and b/RunCat365/resources/runners/parrot/parrot_3.png differ diff --git a/RunCat365/resources/runners/parrot/parrot_4.png b/RunCat365/resources/runners/parrot/parrot_4.png new file mode 100644 index 0000000..06c61f9 Binary files /dev/null and b/RunCat365/resources/runners/parrot/parrot_4.png differ diff --git a/RunCat365/resources/runners/parrot/parrot_5.png b/RunCat365/resources/runners/parrot/parrot_5.png new file mode 100644 index 0000000..7856792 Binary files /dev/null and b/RunCat365/resources/runners/parrot/parrot_5.png differ diff --git a/RunCat365/resources/runners/parrot/parrot_6.png b/RunCat365/resources/runners/parrot/parrot_6.png new file mode 100644 index 0000000..aea3ea6 Binary files /dev/null and b/RunCat365/resources/runners/parrot/parrot_6.png differ diff --git a/RunCat365/resources/runners/parrot/parrot_7.png b/RunCat365/resources/runners/parrot/parrot_7.png new file mode 100644 index 0000000..e9939e1 Binary files /dev/null and b/RunCat365/resources/runners/parrot/parrot_7.png differ diff --git a/RunCat365/resources/runners/parrot/parrot_8.png b/RunCat365/resources/runners/parrot/parrot_8.png new file mode 100644 index 0000000..6bb8ba7 Binary files /dev/null and b/RunCat365/resources/runners/parrot/parrot_8.png differ diff --git a/RunCat365/resources/runners/parrot/parrot_9.png b/RunCat365/resources/runners/parrot/parrot_9.png new file mode 100644 index 0000000..3db65b4 Binary files /dev/null and b/RunCat365/resources/runners/parrot/parrot_9.png differ diff --git a/RunCat365/resources/turtle.png b/RunCat365/resources/turtle.png new file mode 100644 index 0000000..1515de6 Binary files /dev/null and b/RunCat365/resources/turtle.png differ diff --git a/WapForStore/AppPackages/rm-wap-for-store.sh b/WapForStore/AppPackages/rm-wap-for-store.sh new file mode 100755 index 0000000..ab5b235 --- /dev/null +++ b/WapForStore/AppPackages/rm-wap-for-store.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh +# Usage: ./rm-wap-for-store.sh x.y.z.w +[ "$#" -eq 1 ] || { echo "Usage: $0 x.y.z.w" >&2; exit 2; } +v="$1" +rm -rf -- "WapForStore_${v}_Test" +rm -f -- "WapForStore_${v}_x86_x64_arm64_bundle.msixupload" diff --git a/WapForStore/Images/LargeTile.scale-100.png b/WapForStore/Images/LargeTile.scale-100.png new file mode 100644 index 0000000..62d9cf5 Binary files /dev/null and b/WapForStore/Images/LargeTile.scale-100.png differ diff --git a/WapForStore/Images/LargeTile.scale-125.png b/WapForStore/Images/LargeTile.scale-125.png new file mode 100644 index 0000000..bc0e17a Binary files /dev/null and b/WapForStore/Images/LargeTile.scale-125.png differ diff --git a/WapForStore/Images/LargeTile.scale-150.png b/WapForStore/Images/LargeTile.scale-150.png new file mode 100644 index 0000000..4b03a78 Binary files /dev/null and b/WapForStore/Images/LargeTile.scale-150.png differ diff --git a/WapForStore/Images/LargeTile.scale-200.png b/WapForStore/Images/LargeTile.scale-200.png new file mode 100644 index 0000000..5791d05 Binary files /dev/null and b/WapForStore/Images/LargeTile.scale-200.png differ diff --git a/WapForStore/Images/LargeTile.scale-400.png b/WapForStore/Images/LargeTile.scale-400.png new file mode 100644 index 0000000..93a4072 Binary files /dev/null and b/WapForStore/Images/LargeTile.scale-400.png differ diff --git a/WapForStore/Images/LockScreenLogo.scale-200.png b/WapForStore/Images/LockScreenLogo.scale-200.png new file mode 100644 index 0000000..735f57a Binary files /dev/null and b/WapForStore/Images/LockScreenLogo.scale-200.png differ diff --git a/WapForStore/Images/SmallTile.scale-100.png b/WapForStore/Images/SmallTile.scale-100.png new file mode 100644 index 0000000..a1e498e Binary files /dev/null and b/WapForStore/Images/SmallTile.scale-100.png differ diff --git a/WapForStore/Images/SmallTile.scale-125.png b/WapForStore/Images/SmallTile.scale-125.png new file mode 100644 index 0000000..b6003f8 Binary files /dev/null and b/WapForStore/Images/SmallTile.scale-125.png differ diff --git a/WapForStore/Images/SmallTile.scale-150.png b/WapForStore/Images/SmallTile.scale-150.png new file mode 100644 index 0000000..1a9d566 Binary files /dev/null and b/WapForStore/Images/SmallTile.scale-150.png differ diff --git a/WapForStore/Images/SmallTile.scale-200.png b/WapForStore/Images/SmallTile.scale-200.png new file mode 100644 index 0000000..6a88070 Binary files /dev/null and b/WapForStore/Images/SmallTile.scale-200.png differ diff --git a/WapForStore/Images/SmallTile.scale-400.png b/WapForStore/Images/SmallTile.scale-400.png new file mode 100644 index 0000000..bb4a38f Binary files /dev/null and b/WapForStore/Images/SmallTile.scale-400.png differ diff --git a/WapForStore/Images/SplashScreen.scale-100.png b/WapForStore/Images/SplashScreen.scale-100.png new file mode 100644 index 0000000..eda99e0 Binary files /dev/null and b/WapForStore/Images/SplashScreen.scale-100.png differ diff --git a/WapForStore/Images/SplashScreen.scale-125.png b/WapForStore/Images/SplashScreen.scale-125.png new file mode 100644 index 0000000..0a7f5e0 Binary files /dev/null and b/WapForStore/Images/SplashScreen.scale-125.png differ diff --git a/WapForStore/Images/SplashScreen.scale-150.png b/WapForStore/Images/SplashScreen.scale-150.png new file mode 100644 index 0000000..5da64e7 Binary files /dev/null and b/WapForStore/Images/SplashScreen.scale-150.png differ diff --git a/WapForStore/Images/SplashScreen.scale-200.png b/WapForStore/Images/SplashScreen.scale-200.png new file mode 100644 index 0000000..8a5c9b8 Binary files /dev/null and b/WapForStore/Images/SplashScreen.scale-200.png differ diff --git a/WapForStore/Images/SplashScreen.scale-400.png b/WapForStore/Images/SplashScreen.scale-400.png new file mode 100644 index 0000000..e32c43c Binary files /dev/null and b/WapForStore/Images/SplashScreen.scale-400.png differ diff --git a/WapForStore/Images/Square150x150Logo.scale-100.png b/WapForStore/Images/Square150x150Logo.scale-100.png new file mode 100644 index 0000000..ba14b0b Binary files /dev/null and b/WapForStore/Images/Square150x150Logo.scale-100.png differ diff --git a/WapForStore/Images/Square150x150Logo.scale-125.png b/WapForStore/Images/Square150x150Logo.scale-125.png new file mode 100644 index 0000000..2056280 Binary files /dev/null and b/WapForStore/Images/Square150x150Logo.scale-125.png differ diff --git a/WapForStore/Images/Square150x150Logo.scale-150.png b/WapForStore/Images/Square150x150Logo.scale-150.png new file mode 100644 index 0000000..40e9be6 Binary files /dev/null and b/WapForStore/Images/Square150x150Logo.scale-150.png differ diff --git a/WapForStore/Images/Square150x150Logo.scale-200.png b/WapForStore/Images/Square150x150Logo.scale-200.png new file mode 100644 index 0000000..cf49e74 Binary files /dev/null and b/WapForStore/Images/Square150x150Logo.scale-200.png differ diff --git a/WapForStore/Images/Square150x150Logo.scale-400.png b/WapForStore/Images/Square150x150Logo.scale-400.png new file mode 100644 index 0000000..c8583ad Binary files /dev/null and b/WapForStore/Images/Square150x150Logo.scale-400.png differ diff --git a/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png b/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png new file mode 100644 index 0000000..da9524a Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png differ diff --git a/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png b/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png new file mode 100644 index 0000000..b3ff52e Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png differ diff --git a/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png b/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png new file mode 100644 index 0000000..3a98996 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png differ diff --git a/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png b/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png new file mode 100644 index 0000000..88b1955 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png differ diff --git a/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png b/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png new file mode 100644 index 0000000..3e19564 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png differ diff --git a/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-16.png b/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-16.png new file mode 100644 index 0000000..da9524a Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-16.png differ diff --git a/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-256.png b/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-256.png new file mode 100644 index 0000000..3a98996 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-256.png differ diff --git a/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-32.png b/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-32.png new file mode 100644 index 0000000..88b1955 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-32.png differ diff --git a/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-48.png b/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-48.png new file mode 100644 index 0000000..3e19564 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.altform-unplated_targetsize-48.png differ diff --git a/WapForStore/Images/Square44x44Logo.scale-100.png b/WapForStore/Images/Square44x44Logo.scale-100.png new file mode 100644 index 0000000..4fce152 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.scale-100.png differ diff --git a/WapForStore/Images/Square44x44Logo.scale-125.png b/WapForStore/Images/Square44x44Logo.scale-125.png new file mode 100644 index 0000000..660e7fe Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.scale-125.png differ diff --git a/WapForStore/Images/Square44x44Logo.scale-150.png b/WapForStore/Images/Square44x44Logo.scale-150.png new file mode 100644 index 0000000..9c75c24 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.scale-150.png differ diff --git a/WapForStore/Images/Square44x44Logo.scale-200.png b/WapForStore/Images/Square44x44Logo.scale-200.png new file mode 100644 index 0000000..35622d5 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.scale-200.png differ diff --git a/WapForStore/Images/Square44x44Logo.scale-400.png b/WapForStore/Images/Square44x44Logo.scale-400.png new file mode 100644 index 0000000..cfd07d6 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.scale-400.png differ diff --git a/WapForStore/Images/Square44x44Logo.targetsize-16.png b/WapForStore/Images/Square44x44Logo.targetsize-16.png new file mode 100644 index 0000000..9f245fe Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.targetsize-16.png differ diff --git a/WapForStore/Images/Square44x44Logo.targetsize-24.png b/WapForStore/Images/Square44x44Logo.targetsize-24.png new file mode 100644 index 0000000..1b4ca07 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.targetsize-24.png differ diff --git a/WapForStore/Images/Square44x44Logo.targetsize-24_altform-unplated.png b/WapForStore/Images/Square44x44Logo.targetsize-24_altform-unplated.png new file mode 100644 index 0000000..b3ff52e Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.targetsize-24_altform-unplated.png differ diff --git a/WapForStore/Images/Square44x44Logo.targetsize-256.png b/WapForStore/Images/Square44x44Logo.targetsize-256.png new file mode 100644 index 0000000..89627e2 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.targetsize-256.png differ diff --git a/WapForStore/Images/Square44x44Logo.targetsize-32.png b/WapForStore/Images/Square44x44Logo.targetsize-32.png new file mode 100644 index 0000000..2a91660 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.targetsize-32.png differ diff --git a/WapForStore/Images/Square44x44Logo.targetsize-48.png b/WapForStore/Images/Square44x44Logo.targetsize-48.png new file mode 100644 index 0000000..4b9fe68 Binary files /dev/null and b/WapForStore/Images/Square44x44Logo.targetsize-48.png differ diff --git a/WapForStore/Images/StoreLogo.png b/WapForStore/Images/StoreLogo.png new file mode 100644 index 0000000..7385b56 Binary files /dev/null and b/WapForStore/Images/StoreLogo.png differ diff --git a/WapForStore/Images/StoreLogo.scale-100.png b/WapForStore/Images/StoreLogo.scale-100.png new file mode 100644 index 0000000..11865d9 Binary files /dev/null and b/WapForStore/Images/StoreLogo.scale-100.png differ diff --git a/WapForStore/Images/StoreLogo.scale-125.png b/WapForStore/Images/StoreLogo.scale-125.png new file mode 100644 index 0000000..4662d3a Binary files /dev/null and b/WapForStore/Images/StoreLogo.scale-125.png differ diff --git a/WapForStore/Images/StoreLogo.scale-150.png b/WapForStore/Images/StoreLogo.scale-150.png new file mode 100644 index 0000000..ffb631c Binary files /dev/null and b/WapForStore/Images/StoreLogo.scale-150.png differ diff --git a/WapForStore/Images/StoreLogo.scale-200.png b/WapForStore/Images/StoreLogo.scale-200.png new file mode 100644 index 0000000..e4f6e54 Binary files /dev/null and b/WapForStore/Images/StoreLogo.scale-200.png differ diff --git a/WapForStore/Images/StoreLogo.scale-400.png b/WapForStore/Images/StoreLogo.scale-400.png new file mode 100644 index 0000000..fd53b63 Binary files /dev/null and b/WapForStore/Images/StoreLogo.scale-400.png differ diff --git a/WapForStore/Images/Wide310x150Logo.scale-100.png b/WapForStore/Images/Wide310x150Logo.scale-100.png new file mode 100644 index 0000000..90e6111 Binary files /dev/null and b/WapForStore/Images/Wide310x150Logo.scale-100.png differ diff --git a/WapForStore/Images/Wide310x150Logo.scale-125.png b/WapForStore/Images/Wide310x150Logo.scale-125.png new file mode 100644 index 0000000..543fe8a Binary files /dev/null and b/WapForStore/Images/Wide310x150Logo.scale-125.png differ diff --git a/WapForStore/Images/Wide310x150Logo.scale-150.png b/WapForStore/Images/Wide310x150Logo.scale-150.png new file mode 100644 index 0000000..c081f86 Binary files /dev/null and b/WapForStore/Images/Wide310x150Logo.scale-150.png differ diff --git a/WapForStore/Images/Wide310x150Logo.scale-200.png b/WapForStore/Images/Wide310x150Logo.scale-200.png new file mode 100644 index 0000000..eda99e0 Binary files /dev/null and b/WapForStore/Images/Wide310x150Logo.scale-200.png differ diff --git a/WapForStore/Images/Wide310x150Logo.scale-400.png b/WapForStore/Images/Wide310x150Logo.scale-400.png new file mode 100644 index 0000000..8a5c9b8 Binary files /dev/null and b/WapForStore/Images/Wide310x150Logo.scale-400.png differ diff --git a/WapForStore/Package.StoreAssociation.xml b/WapForStore/Package.StoreAssociation.xml new file mode 100644 index 0000000..b30f0a7 --- /dev/null +++ b/WapForStore/Package.StoreAssociation.xml @@ -0,0 +1,371 @@ +<?xml version="1.0" encoding="utf-8"?> +<StoreAssociation xmlns="http://schemas.microsoft.com/appx/2010/storeassociation"> + <Publisher>CN=B2C5F2BD-A1AB-42BE-A228-02E6F800BE2A</Publisher> + <PublisherDisplayName>Studio Kyome</PublisherDisplayName> + <DeveloperAccountType>MSA</DeveloperAccountType> + <GeneratePackageHash>http://www.w3.org/2001/04/xmlenc#sha256</GeneratePackageHash> + <SupportedLocales> + <Language Code="af" InMinimumRequirementSet="true" /> + <Language Code="af-za" InMinimumRequirementSet="true" /> + <Language Code="am" InMinimumRequirementSet="true" /> + <Language Code="am-et" InMinimumRequirementSet="true" /> + <Language Code="ar" InMinimumRequirementSet="true" /> + <Language Code="ar-ae" InMinimumRequirementSet="true" /> + <Language Code="ar-bh" InMinimumRequirementSet="true" /> + <Language Code="ar-dz" InMinimumRequirementSet="true" /> + <Language Code="ar-eg" InMinimumRequirementSet="true" /> + <Language Code="ar-iq" InMinimumRequirementSet="true" /> + <Language Code="ar-jo" InMinimumRequirementSet="true" /> + <Language Code="ar-kw" InMinimumRequirementSet="true" /> + <Language Code="ar-lb" InMinimumRequirementSet="true" /> + <Language Code="ar-ly" InMinimumRequirementSet="true" /> + <Language Code="ar-ma" InMinimumRequirementSet="true" /> + <Language Code="ar-om" InMinimumRequirementSet="true" /> + <Language Code="ar-qa" InMinimumRequirementSet="true" /> + <Language Code="ar-sa" InMinimumRequirementSet="true" /> + <Language Code="ar-sy" InMinimumRequirementSet="true" /> + <Language Code="ar-tn" InMinimumRequirementSet="true" /> + <Language Code="ar-ye" InMinimumRequirementSet="true" /> + <Language Code="as" InMinimumRequirementSet="true" /> + <Language Code="as-in" InMinimumRequirementSet="true" /> + <Language Code="az" InMinimumRequirementSet="true" /> + <Language Code="az-arab" InMinimumRequirementSet="true" /> + <Language Code="az-arab-az" InMinimumRequirementSet="true" /> + <Language Code="az-cyrl" InMinimumRequirementSet="true" /> + <Language Code="az-cyrl-az" InMinimumRequirementSet="true" /> + <Language Code="az-latn" InMinimumRequirementSet="true" /> + <Language Code="az-latn-az" InMinimumRequirementSet="true" /> + <Language Code="be" InMinimumRequirementSet="true" /> + <Language Code="be-by" InMinimumRequirementSet="true" /> + <Language Code="bg" InMinimumRequirementSet="true" /> + <Language Code="bg-bg" InMinimumRequirementSet="true" /> + <Language Code="bn" InMinimumRequirementSet="true" /> + <Language Code="bn-bd" InMinimumRequirementSet="true" /> + <Language Code="bn-in" InMinimumRequirementSet="true" /> + <Language Code="bs" InMinimumRequirementSet="true" /> + <Language Code="bs-cyrl" InMinimumRequirementSet="true" /> + <Language Code="bs-cyrl-ba" InMinimumRequirementSet="true" /> + <Language Code="bs-latn" InMinimumRequirementSet="true" /> + <Language Code="bs-latn-ba" InMinimumRequirementSet="true" /> + <Language Code="ca" InMinimumRequirementSet="true" /> + <Language Code="ca-es" InMinimumRequirementSet="true" /> + <Language Code="ca-es-valencia" InMinimumRequirementSet="true" /> + <Language Code="chr-cher" InMinimumRequirementSet="true" /> + <Language Code="chr-cher-us" InMinimumRequirementSet="true" /> + <Language Code="chr-latn" InMinimumRequirementSet="true" /> + <Language Code="cs" InMinimumRequirementSet="true" /> + <Language Code="cs-cz" InMinimumRequirementSet="true" /> + <Language Code="cy" InMinimumRequirementSet="true" /> + <Language Code="cy-gb" InMinimumRequirementSet="true" /> + <Language Code="da" InMinimumRequirementSet="true" /> + <Language Code="da-dk" InMinimumRequirementSet="true" /> + <Language Code="de" InMinimumRequirementSet="true" /> + <Language Code="de-at" InMinimumRequirementSet="true" /> + <Language Code="de-ch" InMinimumRequirementSet="true" /> + <Language Code="de-de" InMinimumRequirementSet="true" /> + <Language Code="de-li" InMinimumRequirementSet="true" /> + <Language Code="de-lu" InMinimumRequirementSet="true" /> + <Language Code="el" InMinimumRequirementSet="true" /> + <Language Code="el-gr" InMinimumRequirementSet="true" /> + <Language Code="en" InMinimumRequirementSet="true" /> + <Language Code="en-011" InMinimumRequirementSet="true" /> + <Language Code="en-014" InMinimumRequirementSet="true" /> + <Language Code="en-018" InMinimumRequirementSet="true" /> + <Language Code="en-021" InMinimumRequirementSet="true" /> + <Language Code="en-029" InMinimumRequirementSet="true" /> + <Language Code="en-053" InMinimumRequirementSet="true" /> + <Language Code="en-au" InMinimumRequirementSet="true" /> + <Language Code="en-bz" InMinimumRequirementSet="true" /> + <Language Code="en-ca" InMinimumRequirementSet="true" /> + <Language Code="en-gb" InMinimumRequirementSet="true" /> + <Language Code="en-hk" InMinimumRequirementSet="true" /> + <Language Code="en-id" InMinimumRequirementSet="true" /> + <Language Code="en-ie" InMinimumRequirementSet="true" /> + <Language Code="en-in" InMinimumRequirementSet="true" /> + <Language Code="en-jm" InMinimumRequirementSet="true" /> + <Language Code="en-kz" InMinimumRequirementSet="true" /> + <Language Code="en-mt" InMinimumRequirementSet="true" /> + <Language Code="en-my" InMinimumRequirementSet="true" /> + <Language Code="en-nz" InMinimumRequirementSet="true" /> + <Language Code="en-ph" InMinimumRequirementSet="true" /> + <Language Code="en-pk" InMinimumRequirementSet="true" /> + <Language Code="en-sg" InMinimumRequirementSet="true" /> + <Language Code="en-tt" InMinimumRequirementSet="true" /> + <Language Code="en-us" InMinimumRequirementSet="true" /> + <Language Code="en-vn" InMinimumRequirementSet="true" /> + <Language Code="en-za" InMinimumRequirementSet="true" /> + <Language Code="en-zw" InMinimumRequirementSet="true" /> + <Language Code="es" InMinimumRequirementSet="true" /> + <Language Code="es-019" InMinimumRequirementSet="true" /> + <Language Code="es-419" InMinimumRequirementSet="true" /> + <Language Code="es-ar" InMinimumRequirementSet="true" /> + <Language Code="es-bo" InMinimumRequirementSet="true" /> + <Language Code="es-cl" InMinimumRequirementSet="true" /> + <Language Code="es-co" InMinimumRequirementSet="true" /> + <Language Code="es-cr" InMinimumRequirementSet="true" /> + <Language Code="es-do" InMinimumRequirementSet="true" /> + <Language Code="es-ec" InMinimumRequirementSet="true" /> + <Language Code="es-es" InMinimumRequirementSet="true" /> + <Language Code="es-gt" InMinimumRequirementSet="true" /> + <Language Code="es-hn" InMinimumRequirementSet="true" /> + <Language Code="es-mx" InMinimumRequirementSet="true" /> + <Language Code="es-ni" InMinimumRequirementSet="true" /> + <Language Code="es-pa" InMinimumRequirementSet="true" /> + <Language Code="es-pe" InMinimumRequirementSet="true" /> + <Language Code="es-pr" InMinimumRequirementSet="true" /> + <Language Code="es-py" InMinimumRequirementSet="true" /> + <Language Code="es-sv" InMinimumRequirementSet="true" /> + <Language Code="es-us" InMinimumRequirementSet="true" /> + <Language Code="es-uy" InMinimumRequirementSet="true" /> + <Language Code="es-ve" InMinimumRequirementSet="true" /> + <Language Code="et" InMinimumRequirementSet="true" /> + <Language Code="et-ee" InMinimumRequirementSet="true" /> + <Language Code="eu" InMinimumRequirementSet="true" /> + <Language Code="eu-es" InMinimumRequirementSet="true" /> + <Language Code="fa" InMinimumRequirementSet="true" /> + <Language Code="fa-ir" InMinimumRequirementSet="true" /> + <Language Code="fi" InMinimumRequirementSet="true" /> + <Language Code="fi-fi" InMinimumRequirementSet="true" /> + <Language Code="fil" InMinimumRequirementSet="true" /> + <Language Code="fil-latn" InMinimumRequirementSet="true" /> + <Language Code="fil-ph" InMinimumRequirementSet="true" /> + <Language Code="fr" InMinimumRequirementSet="true" /> + <Language Code="fr-011" InMinimumRequirementSet="true" /> + <Language Code="fr-015" InMinimumRequirementSet="true" /> + <Language Code="fr-021" InMinimumRequirementSet="true" /> + <Language Code="fr-029" InMinimumRequirementSet="true" /> + <Language Code="fr-155" InMinimumRequirementSet="true" /> + <Language Code="fr-be" InMinimumRequirementSet="true" /> + <Language Code="fr-ca" InMinimumRequirementSet="true" /> + <Language Code="fr-cd" InMinimumRequirementSet="true" /> + <Language Code="fr-ch" InMinimumRequirementSet="true" /> + <Language Code="fr-ci" InMinimumRequirementSet="true" /> + <Language Code="fr-cm" InMinimumRequirementSet="true" /> + <Language Code="fr-fr" InMinimumRequirementSet="true" /> + <Language Code="fr-ht" InMinimumRequirementSet="true" /> + <Language Code="fr-lu" InMinimumRequirementSet="true" /> + <Language Code="fr-ma" InMinimumRequirementSet="true" /> + <Language Code="fr-mc" InMinimumRequirementSet="true" /> + <Language Code="fr-ml" InMinimumRequirementSet="true" /> + <Language Code="fr-re" InMinimumRequirementSet="true" /> + <Language Code="frc-latn" InMinimumRequirementSet="true" /> + <Language Code="frp-latn" InMinimumRequirementSet="true" /> + <Language Code="ga" InMinimumRequirementSet="true" /> + <Language Code="ga-ie" InMinimumRequirementSet="true" /> + <Language Code="gd-gb" InMinimumRequirementSet="true" /> + <Language Code="gd-latn" InMinimumRequirementSet="true" /> + <Language Code="gl" InMinimumRequirementSet="true" /> + <Language Code="gl-es" InMinimumRequirementSet="true" /> + <Language Code="gu" InMinimumRequirementSet="true" /> + <Language Code="gu-in" InMinimumRequirementSet="true" /> + <Language Code="ha" InMinimumRequirementSet="true" /> + <Language Code="ha-latn" InMinimumRequirementSet="true" /> + <Language Code="ha-latn-ng" InMinimumRequirementSet="true" /> + <Language Code="he" InMinimumRequirementSet="true" /> + <Language Code="he-il" InMinimumRequirementSet="true" /> + <Language Code="hi" InMinimumRequirementSet="true" /> + <Language Code="hi-in" InMinimumRequirementSet="true" /> + <Language Code="hr" InMinimumRequirementSet="true" /> + <Language Code="hr-ba" InMinimumRequirementSet="true" /> + <Language Code="hr-hr" InMinimumRequirementSet="true" /> + <Language Code="hu" InMinimumRequirementSet="true" /> + <Language Code="hu-hu" InMinimumRequirementSet="true" /> + <Language Code="hy" InMinimumRequirementSet="true" /> + <Language Code="hy-am" InMinimumRequirementSet="true" /> + <Language Code="id" InMinimumRequirementSet="true" /> + <Language Code="id-id" InMinimumRequirementSet="true" /> + <Language Code="ig-latn" InMinimumRequirementSet="true" /> + <Language Code="ig-ng" InMinimumRequirementSet="true" /> + <Language Code="is" InMinimumRequirementSet="true" /> + <Language Code="is-is" InMinimumRequirementSet="true" /> + <Language Code="it" InMinimumRequirementSet="true" /> + <Language Code="it-ch" InMinimumRequirementSet="true" /> + <Language Code="it-it" InMinimumRequirementSet="true" /> + <Language Code="iu-cans" InMinimumRequirementSet="true" /> + <Language Code="iu-latn" InMinimumRequirementSet="true" /> + <Language Code="iu-latn-ca" InMinimumRequirementSet="true" /> + <Language Code="ja" InMinimumRequirementSet="true" /> + <Language Code="ja-jp" InMinimumRequirementSet="true" /> + <Language Code="ka" InMinimumRequirementSet="true" /> + <Language Code="ka-ge" InMinimumRequirementSet="true" /> + <Language Code="kk" InMinimumRequirementSet="true" /> + <Language Code="kk-kz" InMinimumRequirementSet="true" /> + <Language Code="km" InMinimumRequirementSet="true" /> + <Language Code="km-kh" InMinimumRequirementSet="true" /> + <Language Code="kn" InMinimumRequirementSet="true" /> + <Language Code="kn-in" InMinimumRequirementSet="true" /> + <Language Code="ko" InMinimumRequirementSet="true" /> + <Language Code="ko-kr" InMinimumRequirementSet="true" /> + <Language Code="kok" InMinimumRequirementSet="true" /> + <Language Code="kok-in" InMinimumRequirementSet="true" /> + <Language Code="ku-arab" InMinimumRequirementSet="true" /> + <Language Code="ku-arab-iq" InMinimumRequirementSet="true" /> + <Language Code="ky-cyrl" InMinimumRequirementSet="true" /> + <Language Code="ky-kg" InMinimumRequirementSet="true" /> + <Language Code="lb" InMinimumRequirementSet="true" /> + <Language Code="lb-lu" InMinimumRequirementSet="true" /> + <Language Code="lo" InMinimumRequirementSet="true" /> + <Language Code="lo-la" InMinimumRequirementSet="true" /> + <Language Code="lt" InMinimumRequirementSet="true" /> + <Language Code="lt-lt" InMinimumRequirementSet="true" /> + <Language Code="lv" InMinimumRequirementSet="true" /> + <Language Code="lv-lv" InMinimumRequirementSet="true" /> + <Language Code="mi" InMinimumRequirementSet="true" /> + <Language Code="mi-latn" InMinimumRequirementSet="true" /> + <Language Code="mi-nz" InMinimumRequirementSet="true" /> + <Language Code="mk" InMinimumRequirementSet="true" /> + <Language Code="mk-mk" InMinimumRequirementSet="true" /> + <Language Code="ml" InMinimumRequirementSet="true" /> + <Language Code="ml-in" InMinimumRequirementSet="true" /> + <Language Code="mn-cyrl" InMinimumRequirementSet="true" /> + <Language Code="mn-mn" InMinimumRequirementSet="true" /> + <Language Code="mn-mong" InMinimumRequirementSet="true" /> + <Language Code="mn-phag" InMinimumRequirementSet="true" /> + <Language Code="mr" InMinimumRequirementSet="true" /> + <Language Code="mr-in" InMinimumRequirementSet="true" /> + <Language Code="ms" InMinimumRequirementSet="true" /> + <Language Code="ms-bn" InMinimumRequirementSet="true" /> + <Language Code="ms-my" InMinimumRequirementSet="true" /> + <Language Code="mt" InMinimumRequirementSet="true" /> + <Language Code="mt-mt" InMinimumRequirementSet="true" /> + <Language Code="nb" InMinimumRequirementSet="true" /> + <Language Code="nb-no" InMinimumRequirementSet="true" /> + <Language Code="ne" InMinimumRequirementSet="true" /> + <Language Code="ne-np" InMinimumRequirementSet="true" /> + <Language Code="nl" InMinimumRequirementSet="true" /> + <Language Code="nl-be" InMinimumRequirementSet="true" /> + <Language Code="nl-nl" InMinimumRequirementSet="true" /> + <Language Code="nn" InMinimumRequirementSet="true" /> + <Language Code="nn-no" InMinimumRequirementSet="true" /> + <Language Code="no" InMinimumRequirementSet="true" /> + <Language Code="no-no" InMinimumRequirementSet="true" /> + <Language Code="nso" InMinimumRequirementSet="true" /> + <Language Code="nso-za" InMinimumRequirementSet="true" /> + <Language Code="om" InMinimumRequirementSet="false" /> + <Language Code="om-et" InMinimumRequirementSet="false" /> + <Language Code="or" InMinimumRequirementSet="true" /> + <Language Code="or-in" InMinimumRequirementSet="true" /> + <Language Code="pa" InMinimumRequirementSet="true" /> + <Language Code="pa-arab" InMinimumRequirementSet="true" /> + <Language Code="pa-arab-pk" InMinimumRequirementSet="true" /> + <Language Code="pa-deva" InMinimumRequirementSet="true" /> + <Language Code="pa-in" InMinimumRequirementSet="true" /> + <Language Code="pl" InMinimumRequirementSet="true" /> + <Language Code="pl-pl" InMinimumRequirementSet="true" /> + <Language Code="prs" InMinimumRequirementSet="true" /> + <Language Code="prs-af" InMinimumRequirementSet="true" /> + <Language Code="prs-arab" InMinimumRequirementSet="true" /> + <Language Code="pt" InMinimumRequirementSet="true" /> + <Language Code="pt-br" InMinimumRequirementSet="true" /> + <Language Code="pt-pt" InMinimumRequirementSet="true" /> + <Language Code="quc-latn" InMinimumRequirementSet="true" /> + <Language Code="qut-gt" InMinimumRequirementSet="true" /> + <Language Code="qut-latn" InMinimumRequirementSet="true" /> + <Language Code="quz" InMinimumRequirementSet="true" /> + <Language Code="quz-bo" InMinimumRequirementSet="true" /> + <Language Code="quz-ec" InMinimumRequirementSet="true" /> + <Language Code="quz-pe" InMinimumRequirementSet="true" /> + <Language Code="ro" InMinimumRequirementSet="true" /> + <Language Code="ro-ro" InMinimumRequirementSet="true" /> + <Language Code="ru" InMinimumRequirementSet="true" /> + <Language Code="ru-ru" InMinimumRequirementSet="true" /> + <Language Code="rw" InMinimumRequirementSet="true" /> + <Language Code="rw-rw" InMinimumRequirementSet="true" /> + <Language Code="sd-arab" InMinimumRequirementSet="true" /> + <Language Code="sd-arab-pk" InMinimumRequirementSet="true" /> + <Language Code="sd-deva" InMinimumRequirementSet="true" /> + <Language Code="si" InMinimumRequirementSet="true" /> + <Language Code="si-lk" InMinimumRequirementSet="true" /> + <Language Code="sk" InMinimumRequirementSet="true" /> + <Language Code="sk-sk" InMinimumRequirementSet="true" /> + <Language Code="sl" InMinimumRequirementSet="true" /> + <Language Code="sl-si" InMinimumRequirementSet="true" /> + <Language Code="sq" InMinimumRequirementSet="true" /> + <Language Code="sq-al" InMinimumRequirementSet="true" /> + <Language Code="sr" InMinimumRequirementSet="true" /> + <Language Code="sr-cyrl" InMinimumRequirementSet="true" /> + <Language Code="sr-cyrl-ba" InMinimumRequirementSet="true" /> + <Language Code="sr-cyrl-cs" InMinimumRequirementSet="true" /> + <Language Code="sr-cyrl-me" InMinimumRequirementSet="true" /> + <Language Code="sr-cyrl-rs" InMinimumRequirementSet="true" /> + <Language Code="sr-latn" InMinimumRequirementSet="true" /> + <Language Code="sr-latn-ba" InMinimumRequirementSet="true" /> + <Language Code="sr-latn-cs" InMinimumRequirementSet="true" /> + <Language Code="sr-latn-me" InMinimumRequirementSet="true" /> + <Language Code="sr-latn-rs" InMinimumRequirementSet="true" /> + <Language Code="sv" InMinimumRequirementSet="true" /> + <Language Code="sv-fi" InMinimumRequirementSet="true" /> + <Language Code="sv-se" InMinimumRequirementSet="true" /> + <Language Code="sw" InMinimumRequirementSet="true" /> + <Language Code="sw-ke" InMinimumRequirementSet="true" /> + <Language Code="ta" InMinimumRequirementSet="true" /> + <Language Code="ta-in" InMinimumRequirementSet="true" /> + <Language Code="te" InMinimumRequirementSet="true" /> + <Language Code="te-in" InMinimumRequirementSet="true" /> + <Language Code="tg-arab" InMinimumRequirementSet="true" /> + <Language Code="tg-cyrl" InMinimumRequirementSet="true" /> + <Language Code="tg-cyrl-tj" InMinimumRequirementSet="true" /> + <Language Code="tg-latn" InMinimumRequirementSet="true" /> + <Language Code="th" InMinimumRequirementSet="true" /> + <Language Code="th-th" InMinimumRequirementSet="true" /> + <Language Code="ti" InMinimumRequirementSet="true" /> + <Language Code="ti-et" InMinimumRequirementSet="true" /> + <Language Code="tk-cyrl" InMinimumRequirementSet="true" /> + <Language Code="tk-cyrl-tr" InMinimumRequirementSet="true" /> + <Language Code="tk-latn" InMinimumRequirementSet="true" /> + <Language Code="tk-latn-tr" InMinimumRequirementSet="true" /> + <Language Code="tk-tm" InMinimumRequirementSet="true" /> + <Language Code="tn" InMinimumRequirementSet="true" /> + <Language Code="tn-bw" InMinimumRequirementSet="true" /> + <Language Code="tn-za" InMinimumRequirementSet="true" /> + <Language Code="tr" InMinimumRequirementSet="true" /> + <Language Code="tr-tr" InMinimumRequirementSet="true" /> + <Language Code="tt-arab" InMinimumRequirementSet="true" /> + <Language Code="tt-cyrl" InMinimumRequirementSet="true" /> + <Language Code="tt-latn" InMinimumRequirementSet="true" /> + <Language Code="tt-ru" InMinimumRequirementSet="true" /> + <Language Code="ug-arab" InMinimumRequirementSet="true" /> + <Language Code="ug-cn" InMinimumRequirementSet="true" /> + <Language Code="ug-cyrl" InMinimumRequirementSet="true" /> + <Language Code="ug-latn" InMinimumRequirementSet="true" /> + <Language Code="uk" InMinimumRequirementSet="true" /> + <Language Code="uk-ua" InMinimumRequirementSet="true" /> + <Language Code="ur" InMinimumRequirementSet="true" /> + <Language Code="ur-pk" InMinimumRequirementSet="true" /> + <Language Code="uz" InMinimumRequirementSet="true" /> + <Language Code="uz-cyrl" InMinimumRequirementSet="true" /> + <Language Code="uz-latn" InMinimumRequirementSet="true" /> + <Language Code="uz-latn-uz" InMinimumRequirementSet="true" /> + <Language Code="vi" InMinimumRequirementSet="true" /> + <Language Code="vi-vn" InMinimumRequirementSet="true" /> + <Language Code="wo" InMinimumRequirementSet="true" /> + <Language Code="wo-sn" InMinimumRequirementSet="true" /> + <Language Code="xh" InMinimumRequirementSet="true" /> + <Language Code="xh-za" InMinimumRequirementSet="true" /> + <Language Code="yo-latn" InMinimumRequirementSet="true" /> + <Language Code="yo-ng" InMinimumRequirementSet="true" /> + <Language Code="zh" InMinimumRequirementSet="true" /> + <Language Code="zh-cn" InMinimumRequirementSet="true" /> + <Language Code="zh-hans" InMinimumRequirementSet="true" /> + <Language Code="zh-hans-cn" InMinimumRequirementSet="true" /> + <Language Code="zh-hans-sg" InMinimumRequirementSet="true" /> + <Language Code="zh-hant" InMinimumRequirementSet="true" /> + <Language Code="zh-hant-hk" InMinimumRequirementSet="true" /> + <Language Code="zh-hant-mo" InMinimumRequirementSet="true" /> + <Language Code="zh-hant-tw" InMinimumRequirementSet="true" /> + <Language Code="zh-hk" InMinimumRequirementSet="true" /> + <Language Code="zh-mo" InMinimumRequirementSet="true" /> + <Language Code="zh-sg" InMinimumRequirementSet="true" /> + <Language Code="zh-tw" InMinimumRequirementSet="true" /> + <Language Code="zu" InMinimumRequirementSet="true" /> + <Language Code="zu-za" InMinimumRequirementSet="true" /> + </SupportedLocales> + <ProductReservedInfo> + <MainPackageIdentityName>StudioKyome.RunCat</MainPackageIdentityName> + <ReservedNames> + <ReservedName>RunCat 365</ReservedName> + </ReservedNames> + </ProductReservedInfo> + <AccountPackageIdentityNames /> + <PackageInfoList LandingUrl="https://devcenterapi.dce.mp.microsoft.com/dashboard/Application?appId=9NW5LPNVWFWJ" /> +</StoreAssociation> \ No newline at end of file diff --git a/WapForStore/Package.appxmanifest b/WapForStore/Package.appxmanifest new file mode 100644 index 0000000..d454db0 --- /dev/null +++ b/WapForStore/Package.appxmanifest @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> + +<Package + xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" + xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" + xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" + xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10" + xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" + IgnorableNamespaces="uap uap5 desktop rescap"> + + <Identity + Name="StudioKyome.RunCat" + Publisher="CN=B2C5F2BD-A1AB-42BE-A228-02E6F800BE2A" + Version="3.6.0.0" /> + + <Properties> + <DisplayName>RunCat 365</DisplayName> + <PublisherDisplayName>Studio Kyome</PublisherDisplayName> + <Logo>Images\StoreLogo.png</Logo> + </Properties> + + <Dependencies> + <TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" /> + <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14393.0" MaxVersionTested="10.0.14393.0" /> + </Dependencies> + + <Resources> + <Resource Language="x-generate"/> + </Resources> + + <Applications> + <Application Id="App" + Executable="$targetnametoken$.exe" + EntryPoint="$targetentrypoint$"> + <uap:VisualElements + DisplayName="RunCat 365" + Description="A cute running cat animation on your taskbar. The cat tells you the CPU usage by running speed." + BackgroundColor="transparent" + Square150x150Logo="Images\Square150x150Logo.png" + Square44x44Logo="Images\Square44x44Logo.png"> + <uap:DefaultTile Wide310x150Logo="Images\Wide310x150Logo.png" Square71x71Logo="Images\SmallTile.png" Square310x310Logo="Images\LargeTile.png"/> + <uap:SplashScreen Image="Images\SplashScreen.png" /> + </uap:VisualElements> + <Extensions> + <uap5:Extension + Category="windows.startupTask"> + <uap5:StartupTask + TaskId="RunCatStartup" + DisplayName="RunCat 365"/> + </uap5:Extension> + <desktop:Extension + Category="windows.fullTrustProcess" /> + </Extensions> + </Application> + </Applications> + + <Capabilities> + <rescap:Capability Name="runFullTrust" /> + <Capability Name="internetClient"/> + </Capabilities> +</Package> diff --git a/WapForStore/PackageIcon.png b/WapForStore/PackageIcon.png new file mode 100644 index 0000000..fd3182d Binary files /dev/null and b/WapForStore/PackageIcon.png differ diff --git a/WapForStore/WapForStore.wapproj b/WapForStore/WapForStore.wapproj new file mode 100644 index 0000000..6ec6204 --- /dev/null +++ b/WapForStore/WapForStore.wapproj @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup Condition="'$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '15.0'"> + <VisualStudioVersion>15.0</VisualStudioVersion> + </PropertyGroup> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|x86"> + <Configuration>Debug</Configuration> + <Platform>x86</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x86"> + <Configuration>Release</Configuration> + <Platform>x86</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|ARM64"> + <Configuration>Debug</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM64"> + <Configuration>Release</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup> + <WapProjPath Condition="'$(WapProjPath)'==''">$(MSBuildExtensionsPath)\Microsoft\DesktopBridge\</WapProjPath> + </PropertyGroup> + <Import Project="$(WapProjPath)\Microsoft.DesktopBridge.props" /> + <PropertyGroup> + <ProjectGuid>5b865d12-1a70-4311-8eba-010e117fe616</ProjectGuid> + <TargetPlatformVersion>10.0.26100.0</TargetPlatformVersion> + <TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion> + <DefaultLanguage>ja-JP</DefaultLanguage> + <AppxPackageSigningEnabled>false</AppxPackageSigningEnabled> + <NoWarn>$(NoWarn);NU1702</NoWarn> + <EntryPointProjectUniqueName>..\RunCat365\RunCat365.csproj</EntryPointProjectUniqueName> + <GenerateTemporaryStoreCertificate>True</GenerateTemporaryStoreCertificate> + <GenerateAppInstallerFile>False</GenerateAppInstallerFile> + <AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm> + <AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision> + <GenerateTestArtifacts>True</GenerateTestArtifacts> + <AppxBundlePlatforms>x86|x64|arm64</AppxBundlePlatforms> + <HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> + <DefaultLanguage>en-US</DefaultLanguage> + <AppxBundle>Always</AppxBundle> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'"> + <DefaultLanguage>en-US</DefaultLanguage> + <AppxBundle>Always</AppxBundle> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'"> + <DefaultLanguage>en-US</DefaultLanguage> + <AppxBundle>Always</AppxBundle> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> + <DefaultLanguage>en-US</DefaultLanguage> + <AppxBundle>Always</AppxBundle> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <DefaultLanguage>en-US</DefaultLanguage> + <AppxBundle>Always</AppxBundle> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <DefaultLanguage>en-US</DefaultLanguage> + <AppxBundle>Always</AppxBundle> + </PropertyGroup> + <ItemGroup> + <AppxManifest Include="Package.appxmanifest"> + <SubType>Designer</SubType> + </AppxManifest> + </ItemGroup> + <ItemGroup> + <Content Include="Images\LargeTile.scale-100.png" /> + <Content Include="Images\LargeTile.scale-125.png" /> + <Content Include="Images\LargeTile.scale-150.png" /> + <Content Include="Images\LargeTile.scale-200.png" /> + <Content Include="Images\LargeTile.scale-400.png" /> + <Content Include="Images\SmallTile.scale-100.png" /> + <Content Include="Images\SmallTile.scale-125.png" /> + <Content Include="Images\SmallTile.scale-150.png" /> + <Content Include="Images\SmallTile.scale-200.png" /> + <Content Include="Images\SmallTile.scale-400.png" /> + <Content Include="Images\SplashScreen.scale-100.png" /> + <Content Include="Images\SplashScreen.scale-125.png" /> + <Content Include="Images\SplashScreen.scale-150.png" /> + <Content Include="Images\SplashScreen.scale-200.png" /> + <Content Include="Images\LockScreenLogo.scale-200.png" /> + <Content Include="Images\SplashScreen.scale-400.png" /> + <Content Include="Images\Square150x150Logo.scale-100.png" /> + <Content Include="Images\Square150x150Logo.scale-125.png" /> + <Content Include="Images\Square150x150Logo.scale-150.png" /> + <Content Include="Images\Square150x150Logo.scale-200.png" /> + <Content Include="Images\Square150x150Logo.scale-400.png" /> + <Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-16.png" /> + <Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-24.png" /> + <Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-256.png" /> + <Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-32.png" /> + <Content Include="Images\Square44x44Logo.altform-lightunplated_targetsize-48.png" /> + <Content Include="Images\Square44x44Logo.altform-unplated_targetsize-16.png" /> + <Content Include="Images\Square44x44Logo.altform-unplated_targetsize-256.png" /> + <Content Include="Images\Square44x44Logo.altform-unplated_targetsize-32.png" /> + <Content Include="Images\Square44x44Logo.altform-unplated_targetsize-48.png" /> + <Content Include="Images\Square44x44Logo.scale-100.png" /> + <Content Include="Images\Square44x44Logo.scale-125.png" /> + <Content Include="Images\Square44x44Logo.scale-150.png" /> + <Content Include="Images\Square44x44Logo.scale-200.png" /> + <Content Include="Images\Square44x44Logo.scale-400.png" /> + <Content Include="Images\Square44x44Logo.targetsize-16.png" /> + <Content Include="Images\Square44x44Logo.targetsize-24.png" /> + <Content Include="Images\Square44x44Logo.targetsize-24_altform-unplated.png" /> + <Content Include="Images\Square44x44Logo.targetsize-256.png" /> + <Content Include="Images\Square44x44Logo.targetsize-32.png" /> + <Content Include="Images\Square44x44Logo.targetsize-48.png" /> + <Content Include="Images\StoreLogo.scale-100.png" /> + <Content Include="Images\StoreLogo.scale-125.png" /> + <Content Include="Images\StoreLogo.scale-150.png" /> + <Content Include="Images\StoreLogo.scale-200.png" /> + <Content Include="Images\StoreLogo.scale-400.png" /> + <Content Include="Images\Wide310x150Logo.scale-100.png" /> + <Content Include="Images\Wide310x150Logo.scale-125.png" /> + <Content Include="Images\Wide310x150Logo.scale-150.png" /> + <Content Include="Images\Wide310x150Logo.scale-200.png" /> + <Content Include="Images\Wide310x150Logo.scale-400.png" /> + <None Include="Package.StoreAssociation.xml" /> + </ItemGroup> + <Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" /> + <ItemGroup> + <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" PrivateAssets="all" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\RunCat365\RunCat365.csproj" /> + </ItemGroup> +</Project> \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..04bbf8d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,62 @@ +# RunCat 365 — GitHub Pages + +The project website published at <https://runcat-dev.github.io/RunCat365/>. + +It is a static site rendered in the browser by [lobster.js](https://hacknock.github.io/lobsterjs/), a Markdown parser loaded from a CDN. There is no build step, bundler, or dependency to install — editing a Markdown file and pushing is all it takes to update the page. + +## Files + +| File | Role | +| :--- | :--- | +| `index.html` | Loader shell for the landing page. Picks the content file by language and hands it to lobster.js. | +| `privacy_policy.html` | Loader shell for the privacy policy. Same mechanism as `index.html`. | +| `content.md` / `content.ja.md` | Landing page content (English / Japanese). | +| `privacy_policy.md` / `privacy_policy.ja.md` | Privacy policy content (English / Japanese). | +| `style.css` | Shared stylesheet targeting lobster.js `lbs-*` classes. | +| `images/` | Screenshots, demo GIF, Microsoft Store badge, and Open Graph thumbnail. | + +The two `.html` files are thin loaders — almost all content lives in the `.md` files. + +## Editing content + +Edit the relevant `.md` file directly. lobster.js renders Markdown plus a few block extensions: + +- `:::header` … `:::` — page title block +- `:::footer` … `:::` — footer block (links, language switcher, copyright) +- `:::warp <name>` … `:::` — a column referenced from a silent table (`~ | [~name] | …`); used for the feature cards and the two-column "What you can monitor" layout +- `:::details <summary>` … `:::` — collapsible FAQ entry +- `![alt](path =600x)` — image with an explicit width + +Any text change is reflected on the next page load — no rebuild required. Keep the English and Japanese files in sync when you change wording or structure. + +## Language switching + +The language is chosen by the `?lang` query parameter, read in the `<script>` of each `.html` loader: + +- no parameter (or anything other than `ja`) → English (`content.md`) +- `?lang=ja` → Japanese (`content.ja.md`) + +The footer of each page links between the two. To add a new language: + +1. Create `content.<lang>.md` (and `privacy_policy.<lang>.md`). +2. Extend the language-selection logic in `index.html` and `privacy_policy.html` to map the new `?lang` value to that file. +3. Add a link to the new language in every footer block. + +## Styling + +`style.css` targets the `lbs-*` class names that lobster.js emits (`.lbs-heading-1`, `.lbs-paragraph`, `.lbs-table-silent`, `.lbs-details`, `.lbs-footer`, …). The privacy policy page carries a `privacy-policy` body class so its title can be styled smaller than the landing-page hero. + +## Previewing locally + +lobster.js fetches the Markdown and its own module over HTTP, so opening the files with `file://` will not work. Serve the directory and open it over `http://`: + +```sh +cd docs +python3 -m http.server 8000 +# then open http://localhost:8000/index.html +# Japanese: http://localhost:8000/index.html?lang=ja +``` + +## Deployment + +The site is served by GitHub Pages from this `docs/` directory on the default branch. Pushing changes here updates the live site; there is nothing to build or deploy manually. diff --git a/docs/content.ja.md b/docs/content.ja.md new file mode 100644 index 0000000..69ed1a9 --- /dev/null +++ b/docs/content.ja.md @@ -0,0 +1,96 @@ +:::header + +# RunCat 365 + +タスクバーでネコ飼ってみませんか? +::: + +ネコの走る速さでデバイスの CPU 負荷がわかります。 +タスクバーをちらっと見るだけで十分です。 + +![RunCat 365 デモ](./images/demo.gif =600x) + +[![Microsoft Store からダウンロード](./images/download_from_microsoft_store_ja.svg)](https://apps.microsoft.com/detail/9nw5lpnvwfwj) + +Microsoft Store で配信中 · [GitHub で見る](https://github.com/runcat-dev/RunCat365) + +## 特長 + +~ | [~load] | [~metrics] | [~custom] | +~ | :--- | :--- | :--- | + +:::warp load + +### ひと目で負荷がわかる + +CPU が忙しくなるほどネコは速く走り、落ち着いているときはゆっくり歩きます。数字を読む必要はありません。走る姿を眺めるだけです。 +::: + +:::warp metrics + +### 豊富なシステムメトリクス + +CPU、GPU、メモリ、温度、ストレージ、ネットワーク。気になる情報をすべてタスクバーから見守れます。 +::: + +:::warp custom + +### 生活に癒しと息抜きを + +作業の合間にふと目に映るだけでちょっとした癒しを提供します。内蔵のミニゲームでひと休みすることもできます。 +::: + +## モニタリングできる項目 + +~ | [~monitor-shot] | [~monitor-list] | +~ | :--- | :--- | + +:::warp monitor-shot +![メトリクスの一覧](./images/overview.png) +::: + +:::warp monitor-list +タスクバーをちらっと見るだけで、気になるメトリクスを確認できます。 + +- CPU 使用率 +- GPU 使用率 +- メモリパフォーマンス +- マシンの温度 +- ストレージ容量 +- ネットワーク速度 + +::: + +## カスタムランナー + +ネコが好みでなくても大丈夫。自分でキーフレームアニメーションを用意すれば自作のランナーを走らせられます。 + +![カスタムランナー](./images/custom_runner.png) + +## ちょっとしたゲーム + +ひと休みしたいときは、スペースキーだけで遊べるシンプルなエンドレスゲームをどうぞ。 + +![エンドレスゲーム](./images/endless_game.png) + +## よくある質問 + +:::details 対応している言語は? +RunCat 365 は 7 つの言語に対応しています。英語、日本語、中国語(簡体字・繁体字)、フランス語、ドイツ語、スペイン語。 +::: + +:::details macOS 版の RunCat と同じもの? +いいえ。RunCat 365 は Windows 版で、Windows Forms で作られ、Microsoft Store で配信されています。RunCat の精神を受け継いでいますが、別のアプリです。 +::: + +:::details バグ報告や機能リクエストはどこで? +[GitHub リポジトリ](https://github.com/runcat-dev/RunCat365)でテンプレートに従って Issue を作成してください。開発者同士の議論には、[RunCat Developers コミュニティ](https://runcat-dev.github.io)をご利用ください。 +::: + +:::footer +[プライバシーポリシー](./privacy_policy.html?lang=ja) · [GitHub](https://github.com/runcat-dev/RunCat365) · [RunCat Developers](https://runcat-dev.github.io) + +[English](./) · **日本語** + +© 2022 Takuto Nakamura (Kyome22) +::: diff --git a/docs/content.md b/docs/content.md new file mode 100644 index 0000000..569be22 --- /dev/null +++ b/docs/content.md @@ -0,0 +1,95 @@ +:::header + +# RunCat 365 + +Cat living in the taskbar. +::: + +The cat's running speed shows you the CPU load of your device — one glance at the taskbar is all it takes. + +![RunCat 365 demo](./images/demo.gif =600x) + +[![Download from Microsoft Store](./images/download_from_microsoft_store_en.svg)](https://apps.microsoft.com/detail/9nw5lpnvwfwj) + +Available on the Microsoft Store · [View on GitHub](https://github.com/runcat-dev/RunCat365) + +## Features + +~ | [~load] | [~metrics] | [~comfort] | +~ | :--- | :--- | :--- | + +:::warp load + +### Load at a glance + +The cat speeds up as your CPU gets busier and slows to a stroll when things are calm. No numbers to read — just watch it run. +::: + +:::warp metrics + +### Rich system metrics + +CPU, GPU, memory, temperature, storage, and network — keep an eye on everything that matters right from the taskbar. +::: + +:::warp comfort + +### A little comfort in your day + +Just catching sight of it between tasks brings a small moment of calm. You can also take a breather with the built-in mini-game. +::: + +## What you can monitor + +~ | [~monitor-shot] | [~monitor-list] | +~ | :--- | :--- | + +:::warp monitor-shot +![Overview of metrics](./images/overview.png) +::: + +:::warp monitor-list +A glance at the taskbar shows you the metrics you care about: + +- CPU usage +- GPU usage +- Memory performance +- Machine temperature +- Storage capacity +- Network speed + +::: + +## Custom runners + +Not a cat person? No problem. Prepare your own keyframe animation and run a runner of your own making. + +![Custom runner](./images/custom_runner.png) + +## A little game + +When you need a break, play a simple endless game controlled with nothing but the space bar. + +![Endless game](./images/endless_game.png) + +## FAQ + +:::details What languages are supported? +RunCat 365 speaks seven languages: English, Japanese, Chinese (Simplified and Traditional), French, German, and Spanish. +::: + +:::details Is this the same as the original RunCat for macOS? +No. RunCat 365 is the Windows edition, built with Windows Forms and distributed via the Microsoft Store. It shares the spirit of RunCat but is a separate app. +::: + +:::details Where do I report a bug or request a feature? +Open an Issue on the [GitHub repository](https://github.com/runcat-dev/RunCat365) following the template. For contributor discussion, the [RunCat Developers community](https://runcat-dev.github.io) is the place to be. +::: + +:::footer +[Privacy Policy](./privacy_policy.html) · [GitHub](https://github.com/runcat-dev/RunCat365) · [RunCat Developers](https://runcat-dev.github.io) + +**English** · [日本語](./?lang=ja) + +© 2022 Takuto Nakamura (Kyome22) +::: diff --git a/docs/images/app_icon.png b/docs/images/app_icon.png new file mode 100644 index 0000000..2a49c86 Binary files /dev/null and b/docs/images/app_icon.png differ diff --git a/docs/images/custom_runner.png b/docs/images/custom_runner.png new file mode 100644 index 0000000..9964085 Binary files /dev/null and b/docs/images/custom_runner.png differ diff --git a/docs/images/demo.gif b/docs/images/demo.gif new file mode 100644 index 0000000..4baf38d Binary files /dev/null and b/docs/images/demo.gif differ diff --git a/docs/images/download_from_microsoft_store_en.svg b/docs/images/download_from_microsoft_store_en.svg new file mode 100644 index 0000000..910ba9b --- /dev/null +++ b/docs/images/download_from_microsoft_store_en.svg @@ -0,0 +1,109 @@ +<svg width="161" height="44" viewBox="0 0 161 44" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="1" y="1" width="159" height="42" rx="4" fill="#202020"/> +<rect x="0.5" y="0.5" width="160" height="43" rx="4.5" stroke="black" stroke-opacity="0.1"/> +<g clip-path="url(#clip0_1049_9370)"> +<g clip-path="url(#clip1_1049_9370)"> +<path d="M10.3438 14.6667C10.3438 14.1604 10.7542 13.75 11.2604 13.75H30.7396C31.2458 13.75 31.6562 14.1604 31.6562 14.6667V28.5313C31.6562 30.6196 29.9633 32.3125 27.875 32.3125H14.125C12.0367 32.3125 10.3438 30.6196 10.3438 28.5313V14.6667Z" fill="url(#paint0_linear_1049_9370)"/> +<mask id="mask0_1049_9370" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="10" y="13" width="22" height="20"> +<path d="M10.3438 14.6667C10.3438 14.1604 10.7542 13.75 11.2604 13.75H30.7396C31.2458 13.75 31.6562 14.1604 31.6562 14.6667V28.5313C31.6562 30.6196 29.9633 32.3125 27.875 32.3125H14.125C12.0367 32.3125 10.3438 30.6196 10.3438 28.5313V14.6667Z" fill="url(#paint1_linear_1049_9370)"/> +</mask> +<g mask="url(#mask0_1049_9370)"> +<g filter="url(#filter0_dd_1049_9370)"> +<path d="M15.8438 14.4375V11.6875H26.1562V14.4375" stroke="url(#paint2_linear_1049_9370)" stroke-width="1.375" stroke-linecap="round"/> +</g> +</g> +<g filter="url(#filter1_dd_1049_9370)"> +<path d="M20.6565 18.5625H16.5315V22.6875H20.6565V18.5625Z" fill="#F25022"/> +<path d="M25.4688 18.5625H21.3438V22.6875H25.4688V18.5625Z" fill="#7FBA00"/> +<path d="M25.4688 23.375H21.3438V27.5H25.4688V23.375Z" fill="#FFB900"/> +<path d="M20.6562 23.375H16.5312V27.5H20.6562V23.375Z" fill="#00A4EF"/> +</g> +<path d="M15.8438 14.4375V12.375C15.8438 11.9953 16.1516 11.6875 16.5313 11.6875H25.4688C25.8484 11.6875 26.1562 11.9953 26.1562 12.375V14.4375" stroke="url(#paint3_linear_1049_9370)" stroke-width="1.375" stroke-linecap="round"/> +<rect x="16.5312" y="11" width="8.9375" height="1.375" fill="url(#paint4_linear_1049_9370)"/> +<mask id="mask1_1049_9370" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="16" y="11" width="10" height="2"> +<rect x="16.5312" y="11" width="8.9375" height="1.375" fill="url(#paint5_linear_1049_9370)"/> +</mask> +<g mask="url(#mask1_1049_9370)"> +<g filter="url(#filter2_dd_1049_9370)"> +<rect x="14.5454" y="11.6877" width="1.98611" height="3.4375" fill="#C4C4C4"/> +</g> +</g> +</g> +</g> +<path d="M38.918 17V9.99805H40.8516C42.0853 9.99805 43.0098 10.2829 43.625 10.8525C44.2435 11.4189 44.5527 12.2718 44.5527 13.4111C44.5527 14.4919 44.2093 15.361 43.5225 16.0186C42.8389 16.6729 41.9225 17 40.7734 17H38.918ZM39.7383 10.7402V16.2578H40.7832C41.7012 16.2578 42.4157 16.012 42.9268 15.5205C43.4378 15.029 43.6934 14.3324 43.6934 13.4307C43.6934 12.5322 43.4541 11.86 42.9756 11.4141C42.5003 10.9648 41.7858 10.7402 40.832 10.7402H39.7383ZM47.9121 17.1172C47.1732 17.1172 46.5824 16.8844 46.1396 16.4189C45.7002 15.9502 45.4805 15.3301 45.4805 14.5586C45.4805 13.722 45.7083 13.0677 46.1641 12.5957C46.6198 12.1237 47.2415 11.8861 48.0293 11.8828C48.7747 11.8796 49.3558 12.109 49.7725 12.5713C50.1924 13.0335 50.4023 13.6699 50.4023 14.4805C50.4023 15.2812 50.1761 15.9209 49.7236 16.3994C49.2744 16.8779 48.6706 17.1172 47.9121 17.1172ZM47.9707 16.4434C48.4883 16.4434 48.8854 16.2757 49.1621 15.9404C49.4421 15.6019 49.582 15.125 49.582 14.5098C49.582 13.8815 49.4421 13.3997 49.1621 13.0645C48.8822 12.7259 48.485 12.5566 47.9707 12.5566C47.4564 12.5566 47.0495 12.7324 46.75 13.084C46.4505 13.4355 46.3008 13.9173 46.3008 14.5293C46.3008 15.1217 46.4521 15.5889 46.7549 15.9307C47.0576 16.2725 47.4629 16.4434 47.9707 16.4434ZM57.9805 12L56.4814 17H55.6514L54.6162 13.4111C54.5902 13.32 54.5723 13.2435 54.5625 13.1816C54.556 13.1165 54.5495 13.0417 54.543 12.957H54.5234C54.5169 13.0417 54.5055 13.1149 54.4893 13.1768C54.473 13.2386 54.4502 13.3167 54.4209 13.4111L53.3027 17H52.502L50.9883 12H51.8281L52.8633 15.7598C52.8796 15.8151 52.8926 15.8802 52.9023 15.9551C52.9154 16.0299 52.9251 16.1146 52.9316 16.209H52.9707C52.9772 16.1374 52.987 16.0641 53 15.9893C53.013 15.9111 53.0326 15.8314 53.0586 15.75L54.2109 12H54.9434L55.9785 15.7695C55.9948 15.8314 56.0078 15.8997 56.0176 15.9746C56.0306 16.0462 56.042 16.1276 56.0518 16.2188H56.0908C56.0941 16.1536 56.1022 16.0837 56.1152 16.0088C56.1315 15.9339 56.151 15.8542 56.1738 15.7695L57.1895 12H57.9805ZM63.0586 17H62.2578V14.1484C62.2578 13.6178 62.1602 13.2207 61.9648 12.957C61.7728 12.6901 61.4831 12.5566 61.0957 12.5566C60.6953 12.5566 60.3633 12.708 60.0996 13.0107C59.8392 13.3102 59.709 13.6895 59.709 14.1484V17H58.9082V12H59.709V12.8301H59.7285C59.9173 12.5143 60.1484 12.2783 60.4219 12.1221C60.6953 11.9626 61.0111 11.8828 61.3691 11.8828C61.916 11.8828 62.3343 12.0602 62.624 12.415C62.9137 12.7666 63.0586 13.276 63.0586 13.9434V17ZM64.5723 17V9.59766H65.373V17H64.5723ZM69.084 17.1172C68.3451 17.1172 67.7542 16.8844 67.3115 16.4189C66.8721 15.9502 66.6523 15.3301 66.6523 14.5586C66.6523 13.722 66.8802 13.0677 67.3359 12.5957C67.7917 12.1237 68.4134 11.8861 69.2012 11.8828C69.9466 11.8796 70.5277 12.109 70.9443 12.5713C71.3643 13.0335 71.5742 13.6699 71.5742 14.4805C71.5742 15.2812 71.348 15.9209 70.8955 16.3994C70.4463 16.8779 69.8424 17.1172 69.084 17.1172ZM69.1426 16.4434C69.6602 16.4434 70.0573 16.2757 70.334 15.9404C70.6139 15.6019 70.7539 15.125 70.7539 14.5098C70.7539 13.8815 70.6139 13.3997 70.334 13.0645C70.054 12.7259 69.6569 12.5566 69.1426 12.5566C68.6283 12.5566 68.2214 12.7324 67.9219 13.084C67.6224 13.4355 67.4727 13.9173 67.4727 14.5293C67.4727 15.1217 67.624 15.5889 67.9268 15.9307C68.2295 16.2725 68.6348 16.4434 69.1426 16.4434ZM76.3936 17H75.5928V16.2188H75.5732C75.4007 16.5182 75.1875 16.7428 74.9336 16.8926C74.6829 17.0423 74.39 17.1172 74.0547 17.1172C73.5859 17.1172 73.2067 16.9805 72.917 16.707C72.6273 16.4336 72.4824 16.0706 72.4824 15.6182C72.4824 15.1917 72.6289 14.8385 72.9219 14.5586C73.2148 14.2786 73.6299 14.0996 74.167 14.0215L75.5928 13.8311V13.7188C75.5928 13.2988 75.4951 13.001 75.2998 12.8252C75.1045 12.6462 74.8441 12.5566 74.5186 12.5566C74.2581 12.5566 74.0075 12.6055 73.7666 12.7031C73.5257 12.8008 73.2995 12.9505 73.0879 13.1523L72.6191 12.6689C72.8991 12.3988 73.1969 12.2002 73.5127 12.0732C73.8285 11.9463 74.18 11.8828 74.5674 11.8828C75.1729 11.8828 75.6286 12.0391 75.9346 12.3516C76.2406 12.6608 76.3936 13.1263 76.3936 13.748V17ZM75.5928 14.9688V14.4707L74.3867 14.6367C74.0189 14.6888 73.7422 14.7816 73.5566 14.915C73.3711 15.0485 73.2783 15.2585 73.2783 15.5449C73.2783 15.8444 73.3646 16.0739 73.5371 16.2334C73.7129 16.3896 73.9261 16.4678 74.1768 16.4678C74.6097 16.4678 74.9531 16.3262 75.207 16.043C75.4642 15.7598 75.5928 15.4017 75.5928 14.9688ZM81.3887 16.1504C81.2031 16.4727 80.9671 16.7152 80.6807 16.8779C80.3975 17.0374 80.0605 17.1172 79.6699 17.1172C79.0417 17.1172 78.5387 16.8942 78.1611 16.4482C77.7868 15.999 77.5996 15.3887 77.5996 14.6172C77.5996 13.7904 77.8079 13.1279 78.2246 12.6299C78.6413 12.1318 79.1963 11.8828 79.8896 11.8828C80.2347 11.8828 80.5309 11.9512 80.7783 12.0879C81.029 12.2214 81.2324 12.4232 81.3887 12.6934H81.4082V9.59766H82.209V17H81.4082V16.1504H81.3887ZM78.4199 14.5781C78.4199 15.1543 78.5534 15.61 78.8203 15.9453C79.0905 16.2773 79.4518 16.4434 79.9043 16.4434C80.3535 16.4434 80.7165 16.2822 80.9932 15.96C81.2699 15.6377 81.4082 15.2308 81.4082 14.7393V14.002C81.4082 13.5983 81.2731 13.2565 81.0029 12.9766C80.736 12.6966 80.3991 12.5566 79.9922 12.5566C79.5039 12.5566 79.1198 12.7357 78.8398 13.0938C78.5599 13.4518 78.4199 13.9466 78.4199 14.5781ZM88.9619 10.252C88.8708 10.2227 88.7878 10.2015 88.7129 10.1885C88.6413 10.1755 88.5648 10.1689 88.4834 10.1689C88.21 10.1689 88.0065 10.2422 87.873 10.3887C87.7396 10.5352 87.6729 10.75 87.6729 11.0332V12H88.8447V12.6836H87.6729V17H86.877V12.6836H86.0225V12H86.877V10.9941C86.877 10.5221 87.0218 10.1543 87.3115 9.89062C87.6012 9.6237 87.9984 9.49023 88.5029 9.49023C88.6266 9.49023 88.7389 9.5 88.8398 9.51953C88.9408 9.53581 89.0449 9.56185 89.1523 9.59766L88.9619 10.252ZM92.1748 12.6836C92.113 12.6641 92.0576 12.651 92.0088 12.6445C91.96 12.6348 91.8981 12.6299 91.8232 12.6299C91.4163 12.6299 91.0957 12.804 90.8613 13.1523C90.627 13.4974 90.5098 13.9303 90.5098 14.4512V17H89.709V12H90.5098V12.9912H90.5293C90.6432 12.6396 90.8174 12.3727 91.0518 12.1904C91.2861 12.0049 91.5563 11.9121 91.8623 11.9121C91.9632 11.9121 92.0479 11.917 92.1162 11.9268C92.1878 11.9365 92.2513 11.9512 92.3066 11.9707L92.1748 12.6836ZM95.2754 17.1172C94.5365 17.1172 93.9456 16.8844 93.5029 16.4189C93.0635 15.9502 92.8438 15.3301 92.8438 14.5586C92.8438 13.722 93.0716 13.0677 93.5273 12.5957C93.9831 12.1237 94.6048 11.8861 95.3926 11.8828C96.138 11.8796 96.7191 12.109 97.1357 12.5713C97.5557 13.0335 97.7656 13.6699 97.7656 14.4805C97.7656 15.2812 97.5394 15.9209 97.0869 16.3994C96.6377 16.8779 96.0339 17.1172 95.2754 17.1172ZM95.334 16.4434C95.8516 16.4434 96.2487 16.2757 96.5254 15.9404C96.8053 15.6019 96.9453 15.125 96.9453 14.5098C96.9453 13.8815 96.8053 13.3997 96.5254 13.0645C96.2454 12.7259 95.8483 12.5566 95.334 12.5566C94.8197 12.5566 94.4128 12.7324 94.1133 13.084C93.8138 13.4355 93.6641 13.9173 93.6641 14.5293C93.6641 15.1217 93.8154 15.5889 94.1182 15.9307C94.4209 16.2725 94.8262 16.4434 95.334 16.4434ZM106.145 17H105.344V14.1289C105.344 13.5755 105.257 13.1751 105.085 12.9277C104.916 12.6803 104.629 12.5566 104.226 12.5566C103.884 12.5566 103.592 12.7129 103.352 13.0254C103.114 13.3379 102.995 13.7122 102.995 14.1484V17H102.194V14.0312C102.194 13.5397 102.1 13.1719 101.911 12.9277C101.722 12.6803 101.438 12.5566 101.057 12.5566C100.705 12.5566 100.415 12.7048 100.188 13.001C99.9596 13.2939 99.8457 13.6764 99.8457 14.1484V17H99.0449V12H99.8457V12.791H99.8652C100.044 12.4883 100.262 12.262 100.52 12.1123C100.78 11.9593 101.079 11.8828 101.418 11.8828C101.76 11.8828 102.058 11.9788 102.312 12.1709C102.565 12.3597 102.74 12.6087 102.834 12.918C103.02 12.5729 103.251 12.3141 103.527 12.1416C103.804 11.9691 104.126 11.8828 104.494 11.8828C105.044 11.8828 105.456 12.0521 105.729 12.3906C106.006 12.7292 106.145 13.2386 106.145 13.9189V17ZM112.785 16.9414C112.648 16.9967 112.513 17.0374 112.38 17.0635C112.25 17.0928 112.102 17.1074 111.936 17.1074C111.538 17.1074 111.226 16.9919 110.998 16.7607C110.773 16.5264 110.661 16.1813 110.661 15.7256V12.6836H109.802V12H110.661V10.7793L111.462 10.5205V12H112.722V12.6836H111.462V15.6328C111.462 15.9193 111.512 16.126 111.613 16.2529C111.717 16.3799 111.872 16.4434 112.077 16.4434C112.175 16.4434 112.269 16.432 112.36 16.4092C112.451 16.3864 112.528 16.3604 112.59 16.3311L112.785 16.9414ZM117.941 17H117.141V14.1191C117.141 13.5983 117.043 13.2077 116.848 12.9473C116.656 12.6868 116.366 12.5566 115.979 12.5566C115.588 12.5566 115.259 12.708 114.992 13.0107C114.725 13.3102 114.592 13.696 114.592 14.168V17H113.791V9.59766H114.592V12.8301H114.611C114.803 12.5143 115.036 12.2783 115.31 12.1221C115.583 11.9626 115.897 11.8828 116.252 11.8828C116.815 11.8828 117.237 12.0521 117.517 12.3906C117.8 12.7292 117.941 13.2386 117.941 13.9189V17ZM123.474 14.6807H119.938C119.951 15.2438 120.1 15.6784 120.383 15.9844C120.669 16.2871 121.07 16.4385 121.584 16.4385C121.809 16.4385 122.033 16.4059 122.258 16.3408C122.486 16.2725 122.71 16.165 122.932 16.0186L123.303 16.5898C123.02 16.7721 122.736 16.9056 122.453 16.9902C122.17 17.0749 121.859 17.1172 121.521 17.1172C120.765 17.1172 120.176 16.8926 119.753 16.4434C119.33 15.9941 119.117 15.3675 119.113 14.5635C119.11 13.7594 119.322 13.1117 119.748 12.6201C120.178 12.1286 120.734 11.8828 121.418 11.8828C122.069 11.8828 122.574 12.096 122.932 12.5225C123.293 12.9456 123.474 13.5332 123.474 14.2852V14.6807ZM122.653 14.0264C122.65 13.5641 122.538 13.2044 122.316 12.9473C122.095 12.6868 121.791 12.5566 121.403 12.5566C121.022 12.5566 120.699 12.6901 120.432 12.957C120.168 13.2207 120.007 13.5771 119.948 14.0264H122.653Z" fill="white"/> +<path d="M51.4219 34H49.5781V26.8516C49.5781 26.5286 49.5859 26.1771 49.6016 25.7969C49.6224 25.4115 49.6484 25.0026 49.6797 24.5703H49.6328C49.5703 24.8672 49.5104 25.1224 49.4531 25.3359C49.401 25.5443 49.349 25.7109 49.2969 25.8359L46.0078 34H44.7266L41.4219 25.9062C41.3802 25.7917 41.3281 25.6224 41.2656 25.3984C41.2083 25.1745 41.1458 24.8984 41.0781 24.5703H41.0312C41.0625 24.9453 41.0833 25.3438 41.0938 25.7656C41.1042 26.1823 41.1094 26.6224 41.1094 27.0859V34H39.3906V22.7969H42.0156L44.9297 30.0781C45.0443 30.3542 45.138 30.6094 45.2109 30.8438C45.2891 31.0729 45.349 31.2865 45.3906 31.4844H45.4375C45.5312 31.1875 45.6198 30.9193 45.7031 30.6797C45.7865 30.4401 45.8646 30.2318 45.9375 30.0547L48.8906 22.7969H51.4219V34ZM54.8984 24.4531C54.5651 24.4531 54.2891 24.3464 54.0703 24.1328C53.8516 23.9193 53.7422 23.6562 53.7422 23.3438C53.7422 23.0208 53.8516 22.7552 54.0703 22.5469C54.2891 22.3385 54.5651 22.2344 54.8984 22.2344C55.237 22.2344 55.5156 22.3385 55.7344 22.5469C55.9531 22.7552 56.0625 23.0208 56.0625 23.3438C56.0625 23.6615 55.9531 23.9271 55.7344 24.1406C55.5156 24.349 55.237 24.4531 54.8984 24.4531ZM55.7969 34H53.9766V26H55.7969V34ZM64.1562 33.5078C63.7604 33.7474 63.3672 33.9193 62.9766 34.0234C62.5859 34.1328 62.1615 34.1875 61.7031 34.1875C60.474 34.1875 59.4896 33.8281 58.75 33.1094C58.0156 32.3906 57.6484 31.4141 57.6484 30.1797C57.6484 28.8516 58.0286 27.7917 58.7891 27C59.5547 26.2083 60.5859 25.8125 61.8828 25.8125C62.3359 25.8125 62.7474 25.862 63.1172 25.9609C63.4922 26.0547 63.8724 26.2109 64.2578 26.4297L63.5469 27.6953C63.276 27.5443 63.0104 27.4349 62.75 27.3672C62.4896 27.2943 62.2057 27.2578 61.8984 27.2578C61.1328 27.2578 60.5417 27.5104 60.125 28.0156C59.7083 28.5208 59.5 29.1901 59.5 30.0234C59.5 30.8984 59.724 31.5703 60.1719 32.0391C60.625 32.5078 61.1927 32.7422 61.875 32.7422C62.0833 32.7422 62.3229 32.7057 62.5938 32.6328C62.8646 32.5547 63.138 32.4323 63.4141 32.2656L64.1562 33.5078ZM70.1484 27.5234C70.0495 27.4974 69.9531 27.4792 69.8594 27.4688C69.7708 27.4531 69.6667 27.4453 69.5469 27.4453C68.8958 27.4453 68.3958 27.6849 68.0469 28.1641C67.6979 28.6432 67.5234 29.2682 67.5234 30.0391V34H65.7031V26H67.5234V27.5391H67.5547C67.7422 26.987 68.0182 26.5703 68.3828 26.2891C68.7474 26.0026 69.1771 25.8594 69.6719 25.8594C69.8125 25.8594 69.9375 25.8672 70.0469 25.8828C70.1615 25.8984 70.2708 25.9219 70.375 25.9531L70.1484 27.5234ZM75.1719 34.1875C73.9219 34.1875 72.9297 33.8151 72.1953 33.0703C71.4661 32.3203 71.1016 31.3177 71.1016 30.0625C71.1068 28.7344 71.487 27.6953 72.2422 26.9453C72.9974 26.1901 74.026 25.8125 75.3281 25.8125C76.5833 25.8073 77.5625 26.1771 78.2656 26.9219C78.974 27.6615 79.3281 28.6641 79.3281 29.9297C79.3281 31.2318 78.9531 32.2682 78.2031 33.0391C77.4583 33.8047 76.4479 34.1875 75.1719 34.1875ZM75.2578 32.7422C75.9818 32.7422 76.5312 32.5052 76.9062 32.0312C77.2865 31.5573 77.4766 30.8724 77.4766 29.9766C77.4766 29.0911 77.2865 28.4167 76.9062 27.9531C76.526 27.4896 75.974 27.2578 75.25 27.2578C74.5365 27.2578 73.974 27.5026 73.5625 27.9922C73.1562 28.4818 72.9531 29.1589 72.9531 30.0234C72.9531 30.8932 73.1562 31.5651 73.5625 32.0391C73.9688 32.5078 74.5339 32.7422 75.2578 32.7422ZM80.4062 33.3594L81.1953 32.0781C81.5078 32.2969 81.8385 32.474 82.1875 32.6094C82.5417 32.7396 82.9245 32.8047 83.3359 32.8047C83.7839 32.8047 84.1302 32.724 84.375 32.5625C84.625 32.3958 84.75 32.1484 84.75 31.8203C84.75 31.5599 84.6458 31.3438 84.4375 31.1719C84.2344 30.9948 83.8854 30.849 83.3906 30.7344C82.3906 30.5 81.6875 30.1901 81.2812 29.8047C80.8802 29.4193 80.6797 28.9115 80.6797 28.2812C80.6797 27.5573 80.9609 26.9661 81.5234 26.5078C82.0859 26.0443 82.8229 25.8125 83.7344 25.8125C84.1875 25.8125 84.6198 25.8724 85.0312 25.9922C85.4427 26.1068 85.8438 26.2917 86.2344 26.5469L85.5 27.7969C85.1562 27.5781 84.8411 27.4219 84.5547 27.3281C84.2682 27.2344 83.974 27.1875 83.6719 27.1875C83.2448 27.1875 82.9115 27.2682 82.6719 27.4297C82.4375 27.5859 82.3203 27.8047 82.3203 28.0859C82.3203 28.3828 82.4401 28.6042 82.6797 28.75C82.9245 28.8906 83.3333 29.0286 83.9062 29.1641C84.7708 29.3724 85.4036 29.6745 85.8047 30.0703C86.2057 30.4609 86.4062 30.9844 86.4062 31.6406C86.4062 32.4167 86.1224 33.0365 85.5547 33.5C84.9922 33.9583 84.2292 34.1875 83.2656 34.1875C82.7448 34.1875 82.2448 34.1198 81.7656 33.9844C81.2917 33.8438 80.8385 33.6354 80.4062 33.3594ZM91.6406 34.1875C90.3906 34.1875 89.3984 33.8151 88.6641 33.0703C87.9349 32.3203 87.5703 31.3177 87.5703 30.0625C87.5755 28.7344 87.9557 27.6953 88.7109 26.9453C89.4661 26.1901 90.4948 25.8125 91.7969 25.8125C93.0521 25.8073 94.0312 26.1771 94.7344 26.9219C95.4427 27.6615 95.7969 28.6641 95.7969 29.9297C95.7969 31.2318 95.4219 32.2682 94.6719 33.0391C93.9271 33.8047 92.9167 34.1875 91.6406 34.1875ZM91.7266 32.7422C92.4505 32.7422 93 32.5052 93.375 32.0312C93.7552 31.5573 93.9453 30.8724 93.9453 29.9766C93.9453 29.0911 93.7552 28.4167 93.375 27.9531C92.9948 27.4896 92.4427 27.2578 91.7188 27.2578C91.0052 27.2578 90.4427 27.5026 90.0312 27.9922C89.625 28.4818 89.4219 29.1589 89.4219 30.0234C89.4219 30.8932 89.625 31.5651 90.0312 32.0391C90.4375 32.5078 91.0026 32.7422 91.7266 32.7422ZM101.977 23.5859C101.831 23.5286 101.698 23.4896 101.578 23.4688C101.458 23.4427 101.328 23.4297 101.188 23.4297C100.802 23.4297 100.51 23.5417 100.312 23.7656C100.12 23.9844 100.023 24.3021 100.023 24.7188V26H101.875V27.4219H100.023V34H98.2109V27.4219H96.8516V26H98.2109V24.625C98.2109 23.8125 98.4714 23.1693 98.9922 22.6953C99.5182 22.2161 100.214 21.9766 101.078 21.9766C101.349 21.9766 101.583 21.9948 101.781 22.0312C101.979 22.0677 102.185 22.1198 102.398 22.1875L101.977 23.5859ZM107.523 33.8906C107.289 33.9896 107.044 34.0625 106.789 34.1094C106.534 34.1562 106.26 34.1797 105.969 34.1797C105.229 34.1797 104.659 33.9844 104.258 33.5938C103.862 33.1979 103.664 32.6094 103.664 31.8281V27.4219H102.32V26H103.664V24.0547L105.477 23.5078V26H107.391V27.4219H105.477V31.6797C105.477 32.0703 105.547 32.3516 105.688 32.5234C105.833 32.6953 106.057 32.7812 106.359 32.7812C106.505 32.7812 106.643 32.7656 106.773 32.7344C106.909 32.6979 107.029 32.6536 107.133 32.6016L107.523 33.8906ZM112.609 33.1953L113.438 31.7656C113.943 32.0781 114.414 32.2995 114.852 32.4297C115.289 32.5547 115.779 32.6172 116.32 32.6172C116.966 32.6172 117.474 32.4792 117.844 32.2031C118.219 31.9219 118.406 31.5208 118.406 31C118.406 30.526 118.25 30.1536 117.938 29.8828C117.63 29.6068 117.107 29.3542 116.367 29.125C115.227 28.7865 114.388 28.3672 113.852 27.8672C113.315 27.3672 113.047 26.6901 113.047 25.8359C113.047 24.9089 113.393 24.1406 114.086 23.5312C114.779 22.9167 115.719 22.6094 116.906 22.6094C117.427 22.6094 117.94 22.6667 118.445 22.7812C118.951 22.8958 119.451 23.0729 119.945 23.3125L119.25 24.7969C118.818 24.5729 118.404 24.4141 118.008 24.3203C117.617 24.2266 117.219 24.1797 116.812 24.1797C116.219 24.1797 115.75 24.3099 115.406 24.5703C115.068 24.8307 114.898 25.1979 114.898 25.6719C114.898 26.0938 115.055 26.4271 115.367 26.6719C115.68 26.9115 116.216 27.1484 116.977 27.3828C118.143 27.7318 118.982 28.1667 119.492 28.6875C120.008 29.2083 120.266 29.9245 120.266 30.8359C120.266 31.8151 119.917 32.6198 119.219 33.25C118.521 33.875 117.526 34.1875 116.234 34.1875C115.589 34.1875 114.958 34.1068 114.344 33.9453C113.729 33.7786 113.151 33.5286 112.609 33.1953ZM125.914 33.8906C125.68 33.9896 125.435 34.0625 125.18 34.1094C124.924 34.1562 124.651 34.1797 124.359 34.1797C123.62 34.1797 123.049 33.9844 122.648 33.5938C122.253 33.1979 122.055 32.6094 122.055 31.8281V27.4219H120.711V26H122.055V24.0547L123.867 23.5078V26H125.781V27.4219H123.867V31.6797C123.867 32.0703 123.938 32.3516 124.078 32.5234C124.224 32.6953 124.448 32.7812 124.75 32.7812C124.896 32.7812 125.034 32.7656 125.164 32.7344C125.299 32.6979 125.419 32.6536 125.523 32.6016L125.914 33.8906ZM130.891 34.1875C129.641 34.1875 128.648 33.8151 127.914 33.0703C127.185 32.3203 126.82 31.3177 126.82 30.0625C126.826 28.7344 127.206 27.6953 127.961 26.9453C128.716 26.1901 129.745 25.8125 131.047 25.8125C132.302 25.8073 133.281 26.1771 133.984 26.9219C134.693 27.6615 135.047 28.6641 135.047 29.9297C135.047 31.2318 134.672 32.2682 133.922 33.0391C133.177 33.8047 132.167 34.1875 130.891 34.1875ZM130.977 32.7422C131.701 32.7422 132.25 32.5052 132.625 32.0312C133.005 31.5573 133.195 30.8724 133.195 29.9766C133.195 29.0911 133.005 28.4167 132.625 27.9531C132.245 27.4896 131.693 27.2578 130.969 27.2578C130.255 27.2578 129.693 27.5026 129.281 27.9922C128.875 28.4818 128.672 29.1589 128.672 30.0234C128.672 30.8932 128.875 31.5651 129.281 32.0391C129.688 32.5078 130.253 32.7422 130.977 32.7422ZM141.352 27.5234C141.253 27.4974 141.156 27.4792 141.062 27.4688C140.974 27.4531 140.87 27.4453 140.75 27.4453C140.099 27.4453 139.599 27.6849 139.25 28.1641C138.901 28.6432 138.727 29.2682 138.727 30.0391V34H136.906V26H138.727V27.5391H138.758C138.945 26.987 139.221 26.5703 139.586 26.2891C139.951 26.0026 140.38 25.8594 140.875 25.8594C141.016 25.8594 141.141 25.8672 141.25 25.8828C141.365 25.8984 141.474 25.9219 141.578 25.9531L141.352 27.5234ZM149.594 30.4766H144.133C144.164 31.2318 144.383 31.8073 144.789 32.2031C145.195 32.5938 145.763 32.7891 146.492 32.7891C146.789 32.7891 147.128 32.7292 147.508 32.6094C147.888 32.4896 148.263 32.2969 148.633 32.0312L149.453 33.1641C148.958 33.5234 148.456 33.7839 147.945 33.9453C147.435 34.1068 146.872 34.1875 146.258 34.1875C145.013 34.1875 144.044 33.8307 143.352 33.1172C142.659 32.3984 142.31 31.3958 142.305 30.1094C142.305 28.8281 142.661 27.7917 143.375 27C144.094 26.2083 145.013 25.8125 146.133 25.8125C147.206 25.8125 148.049 26.151 148.664 26.8281C149.284 27.5 149.594 28.4531 149.594 29.6875V30.4766ZM147.836 29.2188C147.836 28.5312 147.685 28.0156 147.383 27.6719C147.086 27.3229 146.664 27.1484 146.117 27.1484C145.602 27.1484 145.161 27.3359 144.797 27.7109C144.438 28.0807 144.216 28.5833 144.133 29.2188H147.836Z" fill="white"/> +<defs> +<filter id="filter0_dd_1049_9370" x="13.7812" y="10.0833" width="14.4375" height="6.875" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="0.229167"/> +<feGaussianBlur stdDeviation="0.229167"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1049_9370"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="0.458333"/> +<feGaussianBlur stdDeviation="0.6875"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/> +<feBlend mode="normal" in2="effect1_dropShadow_1049_9370" result="effect2_dropShadow_1049_9370"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_1049_9370" result="shape"/> +</filter> +<filter id="filter1_dd_1049_9370" x="15.1562" y="17.6458" width="11.6875" height="11.6875" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="0.229167"/> +<feGaussianBlur stdDeviation="0.229167"/> +<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.6 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1049_9370"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="0.458333"/> +<feGaussianBlur stdDeviation="0.6875"/> +<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.6 0"/> +<feBlend mode="normal" in2="effect1_dropShadow_1049_9370" result="effect2_dropShadow_1049_9370"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_1049_9370" result="shape"/> +</filter> +<filter id="filter2_dd_1049_9370" x="13.1704" y="9.39583" width="15.6594" height="6.18774" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="-0.916667"/> +<feGaussianBlur stdDeviation="0.229167"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1049_9370"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="-0.916667"/> +<feGaussianBlur stdDeviation="0.6875"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/> +<feBlend mode="normal" in2="effect1_dropShadow_1049_9370" result="effect2_dropShadow_1049_9370"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_1049_9370" result="shape"/> +</filter> +<linearGradient id="paint0_linear_1049_9370" x1="15.1875" y1="11.0078" x2="19.6603" y2="31.5495" gradientUnits="userSpaceOnUse"> +<stop offset="0.270833" stop-color="white"/> +<stop offset="1" stop-color="#DFDFDF"/> +</linearGradient> +<linearGradient id="paint1_linear_1049_9370" x1="21" y1="13.75" x2="25.0962" y2="32.9541" gradientUnits="userSpaceOnUse"> +<stop stop-color="#0078D4"/> +<stop offset="1" stop-color="#114A8B"/> +</linearGradient> +<linearGradient id="paint2_linear_1049_9370" x1="21" y1="11.6875" x2="21.1934" y2="14.6494" gradientUnits="userSpaceOnUse"> +<stop stop-color="#28AFEA"/> +<stop offset="1" stop-color="#0078D4"/> +</linearGradient> +<linearGradient id="paint3_linear_1049_9370" x1="21" y1="11.6875" x2="21.1934" y2="14.6494" gradientUnits="userSpaceOnUse"> +<stop stop-color="#30DAFF"/> +<stop offset="1" stop-color="#0094D4"/> +</linearGradient> +<linearGradient id="paint4_linear_1049_9370" x1="20.5035" y1="11" x2="20.5035" y2="12.375" gradientUnits="userSpaceOnUse"> +<stop stop-color="#22BCFF"/> +<stop offset="1" stop-color="#0088F0"/> +</linearGradient> +<linearGradient id="paint5_linear_1049_9370" x1="20.5035" y1="11" x2="20.5035" y2="12.375" gradientUnits="userSpaceOnUse"> +<stop stop-color="#28AFEA"/> +<stop offset="1" stop-color="#3CCBF4"/> +</linearGradient> +<clipPath id="clip0_1049_9370"> +<rect width="22" height="22" fill="white" transform="translate(10 11)"/> +</clipPath> +<clipPath id="clip1_1049_9370"> +<rect width="22" height="22" fill="white" transform="translate(10 11)"/> +</clipPath> +</defs> +</svg> diff --git a/docs/images/download_from_microsoft_store_ja.svg b/docs/images/download_from_microsoft_store_ja.svg new file mode 100644 index 0000000..8d3474c --- /dev/null +++ b/docs/images/download_from_microsoft_store_ja.svg @@ -0,0 +1,109 @@ +<svg width="161" height="44" viewBox="0 0 161 44" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="1" y="1" width="159" height="42" rx="4" fill="#202020"/> +<rect x="0.5" y="0.5" width="160" height="43" rx="4.5" stroke="black" stroke-opacity="0.1"/> +<g clip-path="url(#clip0_1049_9431)"> +<g clip-path="url(#clip1_1049_9431)"> +<path d="M10.3438 14.1667C10.3438 13.6604 10.7542 13.25 11.2604 13.25H30.7396C31.2458 13.25 31.6562 13.6604 31.6562 14.1667V28.0313C31.6562 30.1196 29.9633 31.8125 27.875 31.8125H14.125C12.0367 31.8125 10.3438 30.1196 10.3438 28.0313V14.1667Z" fill="url(#paint0_linear_1049_9431)"/> +<mask id="mask0_1049_9431" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="10" y="13" width="22" height="19"> +<path d="M10.3438 14.1667C10.3438 13.6604 10.7542 13.25 11.2604 13.25H30.7396C31.2458 13.25 31.6562 13.6604 31.6562 14.1667V28.0313C31.6562 30.1196 29.9633 31.8125 27.875 31.8125H14.125C12.0367 31.8125 10.3438 30.1196 10.3438 28.0313V14.1667Z" fill="url(#paint1_linear_1049_9431)"/> +</mask> +<g mask="url(#mask0_1049_9431)"> +<g filter="url(#filter0_dd_1049_9431)"> +<path d="M15.8438 13.9375V11.1875H26.1562V13.9375" stroke="url(#paint2_linear_1049_9431)" stroke-width="1.375" stroke-linecap="round"/> +</g> +</g> +<g filter="url(#filter1_dd_1049_9431)"> +<path d="M20.6565 18.0625H16.5315V22.1875H20.6565V18.0625Z" fill="#F25022"/> +<path d="M25.4688 18.0625H21.3438V22.1875H25.4688V18.0625Z" fill="#7FBA00"/> +<path d="M25.4688 22.875H21.3438V27H25.4688V22.875Z" fill="#FFB900"/> +<path d="M20.6562 22.875H16.5312V27H20.6562V22.875Z" fill="#00A4EF"/> +</g> +<path d="M15.8438 13.9375V11.875C15.8438 11.4953 16.1516 11.1875 16.5313 11.1875H25.4688C25.8484 11.1875 26.1562 11.4953 26.1562 11.875V13.9375" stroke="url(#paint3_linear_1049_9431)" stroke-width="1.375" stroke-linecap="round"/> +<rect x="16.5312" y="10.5" width="8.9375" height="1.375" fill="url(#paint4_linear_1049_9431)"/> +<mask id="mask1_1049_9431" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="16" y="10" width="10" height="2"> +<rect x="16.5312" y="10.5" width="8.9375" height="1.375" fill="url(#paint5_linear_1049_9431)"/> +</mask> +<g mask="url(#mask1_1049_9431)"> +<g filter="url(#filter2_dd_1049_9431)"> +<rect x="14.5454" y="11.1877" width="1.98611" height="3.4375" fill="#C4C4C4"/> +</g> +</g> +</g> +</g> +<path d="M51.4219 21H49.5781V13.8516C49.5781 13.5286 49.5859 13.1771 49.6016 12.7969C49.6224 12.4115 49.6484 12.0026 49.6797 11.5703H49.6328C49.5703 11.8672 49.5104 12.1224 49.4531 12.3359C49.401 12.5443 49.349 12.7109 49.2969 12.8359L46.0078 21H44.7266L41.4219 12.9062C41.3802 12.7917 41.3281 12.6224 41.2656 12.3984C41.2083 12.1745 41.1458 11.8984 41.0781 11.5703H41.0312C41.0625 11.9453 41.0833 12.3438 41.0938 12.7656C41.1042 13.1823 41.1094 13.6224 41.1094 14.0859V21H39.3906V9.79688H42.0156L44.9297 17.0781C45.0443 17.3542 45.138 17.6094 45.2109 17.8438C45.2891 18.0729 45.349 18.2865 45.3906 18.4844H45.4375C45.5312 18.1875 45.6198 17.9193 45.7031 17.6797C45.7865 17.4401 45.8646 17.2318 45.9375 17.0547L48.8906 9.79688H51.4219V21ZM54.8984 11.4531C54.5651 11.4531 54.2891 11.3464 54.0703 11.1328C53.8516 10.9193 53.7422 10.6562 53.7422 10.3438C53.7422 10.0208 53.8516 9.75521 54.0703 9.54688C54.2891 9.33854 54.5651 9.23438 54.8984 9.23438C55.237 9.23438 55.5156 9.33854 55.7344 9.54688C55.9531 9.75521 56.0625 10.0208 56.0625 10.3438C56.0625 10.6615 55.9531 10.9271 55.7344 11.1406C55.5156 11.349 55.237 11.4531 54.8984 11.4531ZM55.7969 21H53.9766V13H55.7969V21ZM64.1562 20.5078C63.7604 20.7474 63.3672 20.9193 62.9766 21.0234C62.5859 21.1328 62.1615 21.1875 61.7031 21.1875C60.474 21.1875 59.4896 20.8281 58.75 20.1094C58.0156 19.3906 57.6484 18.4141 57.6484 17.1797C57.6484 15.8516 58.0286 14.7917 58.7891 14C59.5547 13.2083 60.5859 12.8125 61.8828 12.8125C62.3359 12.8125 62.7474 12.862 63.1172 12.9609C63.4922 13.0547 63.8724 13.2109 64.2578 13.4297L63.5469 14.6953C63.276 14.5443 63.0104 14.4349 62.75 14.3672C62.4896 14.2943 62.2057 14.2578 61.8984 14.2578C61.1328 14.2578 60.5417 14.5104 60.125 15.0156C59.7083 15.5208 59.5 16.1901 59.5 17.0234C59.5 17.8984 59.724 18.5703 60.1719 19.0391C60.625 19.5078 61.1927 19.7422 61.875 19.7422C62.0833 19.7422 62.3229 19.7057 62.5938 19.6328C62.8646 19.5547 63.138 19.4323 63.4141 19.2656L64.1562 20.5078ZM70.1484 14.5234C70.0495 14.4974 69.9531 14.4792 69.8594 14.4688C69.7708 14.4531 69.6667 14.4453 69.5469 14.4453C68.8958 14.4453 68.3958 14.6849 68.0469 15.1641C67.6979 15.6432 67.5234 16.2682 67.5234 17.0391V21H65.7031V13H67.5234V14.5391H67.5547C67.7422 13.987 68.0182 13.5703 68.3828 13.2891C68.7474 13.0026 69.1771 12.8594 69.6719 12.8594C69.8125 12.8594 69.9375 12.8672 70.0469 12.8828C70.1615 12.8984 70.2708 12.9219 70.375 12.9531L70.1484 14.5234ZM75.1719 21.1875C73.9219 21.1875 72.9297 20.8151 72.1953 20.0703C71.4661 19.3203 71.1016 18.3177 71.1016 17.0625C71.1068 15.7344 71.487 14.6953 72.2422 13.9453C72.9974 13.1901 74.026 12.8125 75.3281 12.8125C76.5833 12.8073 77.5625 13.1771 78.2656 13.9219C78.974 14.6615 79.3281 15.6641 79.3281 16.9297C79.3281 18.2318 78.9531 19.2682 78.2031 20.0391C77.4583 20.8047 76.4479 21.1875 75.1719 21.1875ZM75.2578 19.7422C75.9818 19.7422 76.5312 19.5052 76.9062 19.0312C77.2865 18.5573 77.4766 17.8724 77.4766 16.9766C77.4766 16.0911 77.2865 15.4167 76.9062 14.9531C76.526 14.4896 75.974 14.2578 75.25 14.2578C74.5365 14.2578 73.974 14.5026 73.5625 14.9922C73.1562 15.4818 72.9531 16.1589 72.9531 17.0234C72.9531 17.8932 73.1562 18.5651 73.5625 19.0391C73.9688 19.5078 74.5339 19.7422 75.2578 19.7422ZM80.4062 20.3594L81.1953 19.0781C81.5078 19.2969 81.8385 19.474 82.1875 19.6094C82.5417 19.7396 82.9245 19.8047 83.3359 19.8047C83.7839 19.8047 84.1302 19.724 84.375 19.5625C84.625 19.3958 84.75 19.1484 84.75 18.8203C84.75 18.5599 84.6458 18.3438 84.4375 18.1719C84.2344 17.9948 83.8854 17.849 83.3906 17.7344C82.3906 17.5 81.6875 17.1901 81.2812 16.8047C80.8802 16.4193 80.6797 15.9115 80.6797 15.2812C80.6797 14.5573 80.9609 13.9661 81.5234 13.5078C82.0859 13.0443 82.8229 12.8125 83.7344 12.8125C84.1875 12.8125 84.6198 12.8724 85.0312 12.9922C85.4427 13.1068 85.8438 13.2917 86.2344 13.5469L85.5 14.7969C85.1562 14.5781 84.8411 14.4219 84.5547 14.3281C84.2682 14.2344 83.974 14.1875 83.6719 14.1875C83.2448 14.1875 82.9115 14.2682 82.6719 14.4297C82.4375 14.5859 82.3203 14.8047 82.3203 15.0859C82.3203 15.3828 82.4401 15.6042 82.6797 15.75C82.9245 15.8906 83.3333 16.0286 83.9062 16.1641C84.7708 16.3724 85.4036 16.6745 85.8047 17.0703C86.2057 17.4609 86.4062 17.9844 86.4062 18.6406C86.4062 19.4167 86.1224 20.0365 85.5547 20.5C84.9922 20.9583 84.2292 21.1875 83.2656 21.1875C82.7448 21.1875 82.2448 21.1198 81.7656 20.9844C81.2917 20.8438 80.8385 20.6354 80.4062 20.3594ZM91.6406 21.1875C90.3906 21.1875 89.3984 20.8151 88.6641 20.0703C87.9349 19.3203 87.5703 18.3177 87.5703 17.0625C87.5755 15.7344 87.9557 14.6953 88.7109 13.9453C89.4661 13.1901 90.4948 12.8125 91.7969 12.8125C93.0521 12.8073 94.0312 13.1771 94.7344 13.9219C95.4427 14.6615 95.7969 15.6641 95.7969 16.9297C95.7969 18.2318 95.4219 19.2682 94.6719 20.0391C93.9271 20.8047 92.9167 21.1875 91.6406 21.1875ZM91.7266 19.7422C92.4505 19.7422 93 19.5052 93.375 19.0312C93.7552 18.5573 93.9453 17.8724 93.9453 16.9766C93.9453 16.0911 93.7552 15.4167 93.375 14.9531C92.9948 14.4896 92.4427 14.2578 91.7188 14.2578C91.0052 14.2578 90.4427 14.5026 90.0312 14.9922C89.625 15.4818 89.4219 16.1589 89.4219 17.0234C89.4219 17.8932 89.625 18.5651 90.0312 19.0391C90.4375 19.5078 91.0026 19.7422 91.7266 19.7422ZM101.977 10.5859C101.831 10.5286 101.698 10.4896 101.578 10.4688C101.458 10.4427 101.328 10.4297 101.188 10.4297C100.802 10.4297 100.51 10.5417 100.312 10.7656C100.12 10.9844 100.023 11.3021 100.023 11.7188V13H101.875V14.4219H100.023V21H98.2109V14.4219H96.8516V13H98.2109V11.625C98.2109 10.8125 98.4714 10.1693 98.9922 9.69531C99.5182 9.21615 100.214 8.97656 101.078 8.97656C101.349 8.97656 101.583 8.99479 101.781 9.03125C101.979 9.06771 102.185 9.11979 102.398 9.1875L101.977 10.5859ZM107.523 20.8906C107.289 20.9896 107.044 21.0625 106.789 21.1094C106.534 21.1562 106.26 21.1797 105.969 21.1797C105.229 21.1797 104.659 20.9844 104.258 20.5938C103.862 20.1979 103.664 19.6094 103.664 18.8281V14.4219H102.32V13H103.664V11.0547L105.477 10.5078V13H107.391V14.4219H105.477V18.6797C105.477 19.0703 105.547 19.3516 105.688 19.5234C105.833 19.6953 106.057 19.7812 106.359 19.7812C106.505 19.7812 106.643 19.7656 106.773 19.7344C106.909 19.6979 107.029 19.6536 107.133 19.6016L107.523 20.8906ZM112.609 20.1953L113.438 18.7656C113.943 19.0781 114.414 19.2995 114.852 19.4297C115.289 19.5547 115.779 19.6172 116.32 19.6172C116.966 19.6172 117.474 19.4792 117.844 19.2031C118.219 18.9219 118.406 18.5208 118.406 18C118.406 17.526 118.25 17.1536 117.938 16.8828C117.63 16.6068 117.107 16.3542 116.367 16.125C115.227 15.7865 114.388 15.3672 113.852 14.8672C113.315 14.3672 113.047 13.6901 113.047 12.8359C113.047 11.9089 113.393 11.1406 114.086 10.5312C114.779 9.91667 115.719 9.60938 116.906 9.60938C117.427 9.60938 117.94 9.66667 118.445 9.78125C118.951 9.89583 119.451 10.0729 119.945 10.3125L119.25 11.7969C118.818 11.5729 118.404 11.4141 118.008 11.3203C117.617 11.2266 117.219 11.1797 116.812 11.1797C116.219 11.1797 115.75 11.3099 115.406 11.5703C115.068 11.8307 114.898 12.1979 114.898 12.6719C114.898 13.0938 115.055 13.4271 115.367 13.6719C115.68 13.9115 116.216 14.1484 116.977 14.3828C118.143 14.7318 118.982 15.1667 119.492 15.6875C120.008 16.2083 120.266 16.9245 120.266 17.8359C120.266 18.8151 119.917 19.6198 119.219 20.25C118.521 20.875 117.526 21.1875 116.234 21.1875C115.589 21.1875 114.958 21.1068 114.344 20.9453C113.729 20.7786 113.151 20.5286 112.609 20.1953ZM125.914 20.8906C125.68 20.9896 125.435 21.0625 125.18 21.1094C124.924 21.1562 124.651 21.1797 124.359 21.1797C123.62 21.1797 123.049 20.9844 122.648 20.5938C122.253 20.1979 122.055 19.6094 122.055 18.8281V14.4219H120.711V13H122.055V11.0547L123.867 10.5078V13H125.781V14.4219H123.867V18.6797C123.867 19.0703 123.938 19.3516 124.078 19.5234C124.224 19.6953 124.448 19.7812 124.75 19.7812C124.896 19.7812 125.034 19.7656 125.164 19.7344C125.299 19.6979 125.419 19.6536 125.523 19.6016L125.914 20.8906ZM130.891 21.1875C129.641 21.1875 128.648 20.8151 127.914 20.0703C127.185 19.3203 126.82 18.3177 126.82 17.0625C126.826 15.7344 127.206 14.6953 127.961 13.9453C128.716 13.1901 129.745 12.8125 131.047 12.8125C132.302 12.8073 133.281 13.1771 133.984 13.9219C134.693 14.6615 135.047 15.6641 135.047 16.9297C135.047 18.2318 134.672 19.2682 133.922 20.0391C133.177 20.8047 132.167 21.1875 130.891 21.1875ZM130.977 19.7422C131.701 19.7422 132.25 19.5052 132.625 19.0312C133.005 18.5573 133.195 17.8724 133.195 16.9766C133.195 16.0911 133.005 15.4167 132.625 14.9531C132.245 14.4896 131.693 14.2578 130.969 14.2578C130.255 14.2578 129.693 14.5026 129.281 14.9922C128.875 15.4818 128.672 16.1589 128.672 17.0234C128.672 17.8932 128.875 18.5651 129.281 19.0391C129.688 19.5078 130.253 19.7422 130.977 19.7422ZM141.352 14.5234C141.253 14.4974 141.156 14.4792 141.062 14.4688C140.974 14.4531 140.87 14.4453 140.75 14.4453C140.099 14.4453 139.599 14.6849 139.25 15.1641C138.901 15.6432 138.727 16.2682 138.727 17.0391V21H136.906V13H138.727V14.5391H138.758C138.945 13.987 139.221 13.5703 139.586 13.2891C139.951 13.0026 140.38 12.8594 140.875 12.8594C141.016 12.8594 141.141 12.8672 141.25 12.8828C141.365 12.8984 141.474 12.9219 141.578 12.9531L141.352 14.5234ZM149.594 17.4766H144.133C144.164 18.2318 144.383 18.8073 144.789 19.2031C145.195 19.5938 145.763 19.7891 146.492 19.7891C146.789 19.7891 147.128 19.7292 147.508 19.6094C147.888 19.4896 148.263 19.2969 148.633 19.0312L149.453 20.1641C148.958 20.5234 148.456 20.7839 147.945 20.9453C147.435 21.1068 146.872 21.1875 146.258 21.1875C145.013 21.1875 144.044 20.8307 143.352 20.1172C142.659 19.3984 142.31 18.3958 142.305 17.1094C142.305 15.8281 142.661 14.7917 143.375 14C144.094 13.2083 145.013 12.8125 146.133 12.8125C147.206 12.8125 148.049 13.151 148.664 13.8281C149.284 14.5 149.594 15.4531 149.594 16.6875V17.4766ZM147.836 16.2188C147.836 15.5312 147.685 15.0156 147.383 14.6719C147.086 14.3229 146.664 14.1484 146.117 14.1484C145.602 14.1484 145.161 14.3359 144.797 14.7109C144.438 15.0807 144.216 15.5833 144.133 16.2188H147.836Z" fill="white"/> +<path d="M42.38 26.2C42.36 26.2867 42.34 26.3833 42.32 26.49C42.3 26.59 42.28 26.69 42.26 26.79C42.24 26.91 42.21 27.0733 42.17 27.28C42.1367 27.4867 42.1 27.7067 42.06 27.94C42.02 28.1667 41.9733 28.3833 41.92 28.59C41.8533 28.8767 41.77 29.2 41.67 29.56C41.57 29.9133 41.4567 30.29 41.33 30.69C41.2033 31.09 41.06 31.5 40.9 31.92C40.74 32.3333 40.5633 32.7467 40.37 33.16C40.1767 33.5667 39.9667 33.95 39.74 34.31L38.92 33.99C39.1533 33.6833 39.3667 33.34 39.56 32.96C39.76 32.58 39.9433 32.1867 40.11 31.78C40.2767 31.3667 40.4267 30.96 40.56 30.56C40.7 30.16 40.8167 29.79 40.91 29.45C41.0033 29.11 41.0767 28.8167 41.13 28.57C41.2233 28.17 41.3 27.7567 41.36 27.33C41.42 26.8967 41.45 26.4867 41.45 26.1L42.38 26.2ZM45.82 27.26C45.98 27.4667 46.1467 27.7233 46.32 28.03C46.5 28.3367 46.6733 28.6633 46.84 29.01C47.0133 29.3567 47.17 29.69 47.31 30.01C47.45 30.33 47.5633 30.6067 47.65 30.84L46.87 31.21C46.7967 30.9567 46.6967 30.6667 46.57 30.34C46.4433 30.0067 46.2967 29.67 46.13 29.33C45.97 28.9833 45.8 28.66 45.62 28.36C45.4467 28.0533 45.27 27.7967 45.09 27.59L45.82 27.26ZM38.78 28.39C38.9333 28.4033 39.08 28.41 39.22 28.41C39.36 28.4033 39.51 28.3967 39.67 28.39C39.8233 28.3833 40.0167 28.3733 40.25 28.36C40.4833 28.34 40.7367 28.32 41.01 28.3C41.29 28.28 41.5633 28.26 41.83 28.24C42.1033 28.2133 42.3533 28.1933 42.58 28.18C42.8133 28.16 42.9967 28.15 43.13 28.15C43.43 28.15 43.7 28.1967 43.94 28.29C44.1867 28.3833 44.3833 28.5533 44.53 28.8C44.6767 29.0467 44.75 29.3933 44.75 29.84C44.75 30.2333 44.73 30.66 44.69 31.12C44.6567 31.5733 44.5967 32.0067 44.51 32.42C44.4233 32.8333 44.31 33.1767 44.17 33.45C44.0167 33.79 43.8167 34.0133 43.57 34.12C43.3233 34.2333 43.0367 34.29 42.71 34.29C42.5233 34.29 42.32 34.2733 42.1 34.24C41.88 34.2133 41.69 34.18 41.53 34.14L41.4 33.31C41.5467 33.35 41.6933 33.3867 41.84 33.42C41.9933 33.4533 42.14 33.4767 42.28 33.49C42.42 33.5033 42.5367 33.51 42.63 33.51C42.81 33.51 42.9733 33.48 43.12 33.42C43.2733 33.3533 43.4 33.2133 43.5 33C43.6067 32.7733 43.6933 32.49 43.76 32.15C43.8333 31.8033 43.8867 31.4367 43.92 31.05C43.96 30.6633 43.98 30.2933 43.98 29.94C43.98 29.6267 43.9367 29.3967 43.85 29.25C43.7633 29.0967 43.64 28.9933 43.48 28.94C43.32 28.88 43.1333 28.85 42.92 28.85C42.7467 28.85 42.5167 28.8633 42.23 28.89C41.9433 28.91 41.6367 28.9367 41.31 28.97C40.99 29.0033 40.69 29.0367 40.41 29.07C40.13 29.0967 39.9133 29.12 39.76 29.14C39.6467 29.1533 39.5 29.1733 39.32 29.2C39.1467 29.22 38.9933 29.24 38.86 29.26L38.78 28.39ZM51.35 26.16C51.5633 26.2267 51.84 26.3 52.18 26.38C52.5267 26.46 52.89 26.54 53.27 26.62C53.6567 26.6933 54.0233 26.76 54.37 26.82C54.7167 26.8733 55 26.91 55.22 26.93L55.03 27.7C54.8433 27.6733 54.6133 27.6367 54.34 27.59C54.0733 27.5433 53.7867 27.49 53.48 27.43C53.1733 27.37 52.87 27.31 52.57 27.25C52.27 27.1833 51.9967 27.1233 51.75 27.07C51.5033 27.01 51.3033 26.96 51.15 26.92L51.35 26.16ZM51.13 27.98C51.0967 28.1533 51.06 28.3667 51.02 28.62C50.9867 28.8667 50.95 29.1267 50.91 29.4C50.87 29.6733 50.8333 29.9333 50.8 30.18C50.7667 30.4267 50.7367 30.63 50.71 30.79C51.1967 30.37 51.71 30.0633 52.25 29.87C52.7967 29.67 53.37 29.57 53.97 29.57C54.4833 29.57 54.93 29.6633 55.31 29.85C55.69 30.03 55.9867 30.28 56.2 30.6C56.4133 30.9133 56.52 31.2667 56.52 31.66C56.52 32.1067 56.4167 32.5133 56.21 32.88C56.01 33.24 55.6933 33.5433 55.26 33.79C54.8333 34.0367 54.2867 34.21 53.62 34.31C52.9533 34.4167 52.1533 34.43 51.22 34.35L50.98 33.53C52.04 33.6567 52.9167 33.6533 53.61 33.52C54.3033 33.38 54.82 33.1467 55.16 32.82C55.5067 32.4867 55.68 32.0933 55.68 31.64C55.68 31.3733 55.6033 31.1367 55.45 30.93C55.2967 30.7233 55.0867 30.5633 54.82 30.45C54.56 30.33 54.26 30.27 53.92 30.27C53.2667 30.27 52.6733 30.39 52.14 30.63C51.6133 30.8633 51.1733 31.19 50.82 31.61C50.7467 31.6967 50.6867 31.7833 50.64 31.87C50.5933 31.95 50.5533 32.03 50.52 32.11L49.78 31.93C49.82 31.7433 49.86 31.52 49.9 31.26C49.94 30.9933 49.98 30.71 50.02 30.41C50.06 30.1033 50.0967 29.7967 50.13 29.49C50.17 29.1833 50.2033 28.89 50.23 28.61C50.2567 28.3233 50.2767 28.0767 50.29 27.87L51.13 27.98ZM65.65 25.94C65.7367 26.0533 65.83 26.1933 65.93 26.36C66.03 26.52 66.1267 26.6833 66.22 26.85C66.3133 27.0167 66.3967 27.17 66.47 27.31L65.93 27.55C65.83 27.35 65.7033 27.1167 65.55 26.85C65.3967 26.5833 65.2533 26.3567 65.12 26.17L65.65 25.94ZM66.75 25.54C66.8367 25.66 66.93 25.8033 67.03 25.97C67.1367 26.1367 67.24 26.3033 67.34 26.47C67.44 26.63 67.5233 26.7733 67.59 26.9L67.05 27.14C66.9433 26.92 66.8133 26.6833 66.66 26.43C66.5067 26.17 66.36 25.9467 66.22 25.76L66.75 25.54ZM61.83 29.65C62.1567 29.8367 62.4933 30.0433 62.84 30.27C63.1933 30.4967 63.5467 30.7333 63.9 30.98C64.2533 31.2267 64.5867 31.4667 64.9 31.7C65.22 31.9333 65.5033 32.15 65.75 32.35L65.16 33.04C64.9333 32.8333 64.6633 32.6067 64.35 32.36C64.0367 32.1067 63.7 31.8533 63.34 31.6C62.9867 31.34 62.6333 31.0933 62.28 30.86C61.9267 30.62 61.5967 30.4033 61.29 30.21L61.83 29.65ZM66.41 27.83C66.3633 27.8967 66.32 27.9833 66.28 28.09C66.24 28.19 66.2067 28.28 66.18 28.36C66.08 28.7 65.9433 29.0667 65.77 29.46C65.6033 29.8533 65.4 30.25 65.16 30.65C64.9267 31.05 64.6633 31.43 64.37 31.79C63.9167 32.35 63.3533 32.8833 62.68 33.39C62.0133 33.8967 61.2067 34.3267 60.26 34.68L59.54 34.05C60.1667 33.8633 60.7333 33.6267 61.24 33.34C61.7467 33.0533 62.2033 32.74 62.61 32.4C63.0167 32.06 63.37 31.7133 63.67 31.36C63.93 31.0467 64.1667 30.71 64.38 30.35C64.6 29.9833 64.79 29.6167 64.95 29.25C65.11 28.8833 65.2267 28.5467 65.3 28.24H61.59L61.92 27.52H65.09C65.2233 27.52 65.3467 27.5133 65.46 27.5C65.5733 27.48 65.67 27.4567 65.75 27.43L66.41 27.83ZM63.04 26.38C62.9533 26.5133 62.87 26.6567 62.79 26.81C62.71 26.9567 62.6433 27.0767 62.59 27.17C62.39 27.53 62.1333 27.93 61.82 28.37C61.5067 28.81 61.1367 29.2467 60.71 29.68C60.2833 30.1133 59.8033 30.5133 59.27 30.88L58.6 30.37C59.1733 30.01 59.67 29.62 60.09 29.2C60.5167 28.7733 60.87 28.3567 61.15 27.95C61.4367 27.5433 61.6567 27.1867 61.81 26.88C61.8633 26.7933 61.9233 26.6733 61.99 26.52C62.0567 26.3667 62.1033 26.2233 62.13 26.09L63.04 26.38ZM73.41 25.99C73.39 26.1433 73.3733 26.28 73.36 26.4C73.3533 26.52 73.35 26.6333 73.35 26.74C73.35 26.82 73.35 26.9433 73.35 27.11C73.35 27.2767 73.35 27.45 73.35 27.63C73.35 27.81 73.35 27.96 73.35 28.08H72.5C72.5 27.9467 72.5 27.79 72.5 27.61C72.5 27.43 72.5 27.26 72.5 27.1C72.5 26.94 72.5 26.82 72.5 26.74C72.5 26.6333 72.4967 26.52 72.49 26.4C72.49 26.28 72.4767 26.1433 72.45 25.99H73.41ZM76.82 27.93C76.7933 28.0033 76.7667 28.1 76.74 28.22C76.7133 28.3333 76.69 28.4367 76.67 28.53C76.6433 28.73 76.6 28.9533 76.54 29.2C76.4867 29.4467 76.42 29.7067 76.34 29.98C76.26 30.2467 76.1667 30.5133 76.06 30.78C75.9533 31.04 75.83 31.2933 75.69 31.54C75.4433 31.9733 75.1267 32.3733 74.74 32.74C74.3533 33.1067 73.9067 33.4267 73.4 33.7C72.9 33.9733 72.3433 34.1967 71.73 34.37L71.08 33.62C71.2467 33.5933 71.42 33.5567 71.6 33.51C71.7867 33.4567 71.96 33.4 72.12 33.34C72.4533 33.2333 72.79 33.0833 73.13 32.89C73.47 32.69 73.7867 32.46 74.08 32.2C74.38 31.9333 74.63 31.6433 74.83 31.33C75.01 31.0433 75.1633 30.73 75.29 30.39C75.4233 30.05 75.53 29.7067 75.61 29.36C75.69 29.0133 75.7467 28.6967 75.78 28.41H70.19C70.19 28.5167 70.19 28.6533 70.19 28.82C70.19 28.9867 70.19 29.1633 70.19 29.35C70.19 29.53 70.19 29.7 70.19 29.86C70.19 30.0133 70.19 30.1267 70.19 30.2C70.19 30.2933 70.1933 30.3967 70.2 30.51C70.2067 30.6233 70.2167 30.72 70.23 30.8H69.36C69.3733 30.7067 69.38 30.6 69.38 30.48C69.3867 30.36 69.39 30.2533 69.39 30.16C69.39 30.08 69.39 29.96 69.39 29.8C69.39 29.64 69.39 29.47 69.39 29.29C69.39 29.1033 69.39 28.93 69.39 28.77C69.39 28.61 69.39 28.4867 69.39 28.4C69.39 28.2933 69.3867 28.1667 69.38 28.02C69.38 27.8733 69.3733 27.7433 69.36 27.63C69.5067 27.6433 69.6533 27.6533 69.8 27.66C69.9533 27.6667 70.1167 27.67 70.29 27.67H75.59C75.7767 27.67 75.92 27.6633 76.02 27.65C76.1267 27.63 76.2133 27.61 76.28 27.59L76.82 27.93ZM80.27 26.67C80.45 26.79 80.6567 26.94 80.89 27.12C81.1233 27.2933 81.36 27.48 81.6 27.68C81.8467 27.8733 82.0767 28.0633 82.29 28.25C82.5033 28.4367 82.68 28.6 82.82 28.74L82.19 29.37C82.0633 29.2433 81.9 29.0867 81.7 28.9C81.5 28.7133 81.28 28.52 81.04 28.32C80.8 28.1133 80.5633 27.92 80.33 27.74C80.0967 27.5533 79.8867 27.4 79.7 27.28L80.27 26.67ZM79.41 33.37C79.9967 33.2833 80.53 33.1633 81.01 33.01C81.4967 32.8567 81.94 32.6833 82.34 32.49C82.74 32.2967 83.0967 32.1033 83.41 31.91C83.9233 31.59 84.3967 31.2233 84.83 30.81C85.2633 30.39 85.6433 29.9567 85.97 29.51C86.2967 29.0633 86.5567 28.6367 86.75 28.23L87.23 29.08C87.0033 29.4933 86.7233 29.9133 86.39 30.34C86.0567 30.7667 85.68 31.1767 85.26 31.57C84.84 31.9633 84.3767 32.32 83.87 32.64C83.5367 32.8467 83.17 33.05 82.77 33.25C82.3767 33.45 81.9433 33.6333 81.47 33.8C81.0033 33.96 80.4933 34.09 79.94 34.19L79.41 33.37ZM89.46 27.15C89.66 27.1567 89.83 27.1633 89.97 27.17C90.11 27.17 90.2367 27.17 90.35 27.17C90.43 27.17 90.5767 27.17 90.79 27.17C91.0033 27.17 91.26 27.17 91.56 27.17C91.8667 27.17 92.1933 27.17 92.54 27.17C92.8867 27.17 93.23 27.17 93.57 27.17C93.9167 27.17 94.24 27.17 94.54 27.17C94.84 27.17 95.0933 27.17 95.3 27.17C95.5067 27.17 95.6467 27.17 95.72 27.17C95.8267 27.17 95.96 27.17 96.12 27.17C96.2867 27.1633 96.4467 27.1567 96.6 27.15C96.5933 27.27 96.5867 27.4 96.58 27.54C96.58 27.6733 96.58 27.8033 96.58 27.93C96.58 27.9967 96.58 28.13 96.58 28.33C96.58 28.5233 96.58 28.76 96.58 29.04C96.58 29.32 96.58 29.6233 96.58 29.95C96.58 30.27 96.58 30.59 96.58 30.91C96.58 31.23 96.58 31.5267 96.58 31.8C96.58 32.0733 96.58 32.3067 96.58 32.5C96.58 32.6867 96.58 32.8067 96.58 32.86C96.58 32.9333 96.58 33.0333 96.58 33.16C96.58 33.2867 96.58 33.4133 96.58 33.54C96.5867 33.6667 96.59 33.78 96.59 33.88C96.5967 33.9733 96.6 34.0367 96.6 34.07H95.74C95.74 34.03 95.74 33.95 95.74 33.83C95.7467 33.7033 95.75 33.5633 95.75 33.41C95.7567 33.25 95.76 33.1 95.76 32.96C95.76 32.9133 95.76 32.7867 95.76 32.58C95.76 32.3733 95.76 32.12 95.76 31.82C95.76 31.5133 95.76 31.1867 95.76 30.84C95.76 30.4867 95.76 30.1433 95.76 29.81C95.76 29.47 95.76 29.16 95.76 28.88C95.76 28.6 95.76 28.3767 95.76 28.21C95.76 28.0433 95.76 27.96 95.76 27.96H90.29C90.29 27.96 90.29 28.0433 90.29 28.21C90.29 28.37 90.29 28.59 90.29 28.87C90.29 29.15 90.29 29.46 90.29 29.8C90.29 30.1333 90.29 30.4733 90.29 30.82C90.29 31.1667 90.29 31.4933 90.29 31.8C90.29 32.1 90.29 32.3533 90.29 32.56C90.29 32.7667 90.29 32.9 90.29 32.96C90.29 33.0467 90.29 33.1467 90.29 33.26C90.29 33.3733 90.29 33.4867 90.29 33.6C90.2967 33.7067 90.3 33.8033 90.3 33.89C90.3067 33.9767 90.31 34.0367 90.31 34.07H89.45C89.45 34.0367 89.45 33.9733 89.45 33.88C89.4567 33.78 89.46 33.6667 89.46 33.54C89.4667 33.4133 89.47 33.29 89.47 33.17C89.47 33.0433 89.47 32.9367 89.47 32.85C89.47 32.7967 89.47 32.6733 89.47 32.48C89.47 32.2867 89.47 32.05 89.47 31.77C89.47 31.49 89.47 31.19 89.47 30.87C89.47 30.5433 89.47 30.22 89.47 29.9C89.47 29.5733 89.47 29.2733 89.47 29C89.47 28.72 89.47 28.4867 89.47 28.3C89.47 28.1067 89.47 27.9833 89.47 27.93C89.47 27.81 89.47 27.68 89.47 27.54C89.47 27.4 89.4667 27.27 89.46 27.15ZM96.08 32.71V33.49H89.9V32.71H96.08ZM99.02 29.67C99.12 29.6767 99.2433 29.6867 99.39 29.7C99.5367 29.7067 99.6967 29.7133 99.87 29.72C100.05 29.72 100.23 29.72 100.41 29.72C100.517 29.72 100.68 29.72 100.9 29.72C101.127 29.72 101.39 29.72 101.69 29.72C101.99 29.72 102.307 29.72 102.64 29.72C102.98 29.72 103.317 29.72 103.65 29.72C103.99 29.72 104.31 29.72 104.61 29.72C104.917 29.72 105.18 29.72 105.4 29.72C105.62 29.72 105.783 29.72 105.89 29.72C106.143 29.72 106.36 29.7133 106.54 29.7C106.72 29.6867 106.863 29.6767 106.97 29.67V30.65C106.87 30.6433 106.72 30.6333 106.52 30.62C106.327 30.6067 106.12 30.6 105.9 30.6C105.793 30.6 105.627 30.6 105.4 30.6C105.173 30.6 104.91 30.6 104.61 30.6C104.317 30.6 104 30.6 103.66 30.6C103.32 30.6 102.98 30.6 102.64 30.6C102.307 30.6 101.99 30.6 101.69 30.6C101.397 30.6 101.137 30.6 100.91 30.6C100.683 30.6 100.517 30.6 100.41 30.6C100.137 30.6 99.8767 30.6067 99.63 30.62C99.3833 30.6267 99.18 30.6367 99.02 30.65V29.67ZM114.56 26.8C114.647 26.92 114.747 27.07 114.86 27.25C114.973 27.4233 115.083 27.6033 115.19 27.79C115.303 27.9767 115.397 28.15 115.47 28.31L114.9 28.57C114.82 28.3967 114.73 28.2233 114.63 28.05C114.537 27.87 114.437 27.6967 114.33 27.53C114.23 27.3567 114.123 27.1967 114.01 27.05L114.56 26.8ZM115.77 26.3C115.857 26.4133 115.957 26.5567 116.07 26.73C116.19 26.9033 116.307 27.0833 116.42 27.27C116.533 27.4567 116.63 27.6267 116.71 27.78L116.15 28.06C116.063 27.8867 115.97 27.7133 115.87 27.54C115.77 27.3667 115.663 27.1967 115.55 27.03C115.443 26.8633 115.333 26.7067 115.22 26.56L115.77 26.3ZM111.05 33.25C111.05 33.1567 111.05 32.97 111.05 32.69C111.05 32.41 111.05 32.0767 111.05 31.69C111.05 31.2967 111.05 30.8833 111.05 30.45C111.05 30.0167 111.05 29.5967 111.05 29.19C111.05 28.7833 111.05 28.4233 111.05 28.11C111.05 27.7967 111.05 27.57 111.05 27.43C111.05 27.29 111.043 27.12 111.03 26.92C111.023 26.72 111.003 26.5433 110.97 26.39H111.96C111.94 26.5433 111.923 26.72 111.91 26.92C111.897 27.1133 111.89 27.2833 111.89 27.43C111.89 27.69 111.89 28.0033 111.89 28.37C111.89 28.73 111.89 29.1167 111.89 29.53C111.89 29.9367 111.89 30.3433 111.89 30.75C111.89 31.15 111.89 31.5267 111.89 31.88C111.89 32.2267 111.89 32.5233 111.89 32.77C111.89 33.01 111.89 33.17 111.89 33.25C111.89 33.35 111.89 33.4733 111.89 33.62C111.897 33.7667 111.907 33.91 111.92 34.05C111.933 34.1967 111.943 34.3233 111.95 34.43H110.99C111.01 34.27 111.023 34.08 111.03 33.86C111.043 33.64 111.05 33.4367 111.05 33.25ZM111.71 29.01C112.037 29.11 112.4 29.23 112.8 29.37C113.2 29.5033 113.603 29.6467 114.01 29.8C114.423 29.9533 114.813 30.11 115.18 30.27C115.553 30.4233 115.88 30.57 116.16 30.71L115.81 31.56C115.523 31.4067 115.2 31.25 114.84 31.09C114.487 30.93 114.12 30.7733 113.74 30.62C113.36 30.4667 112.993 30.33 112.64 30.21C112.293 30.09 111.983 29.9867 111.71 29.9V29.01Z" fill="white"/> +<defs> +<filter id="filter0_dd_1049_9431" x="13.7812" y="9.58333" width="14.4375" height="6.875" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="0.229167"/> +<feGaussianBlur stdDeviation="0.229167"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1049_9431"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="0.458333"/> +<feGaussianBlur stdDeviation="0.6875"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/> +<feBlend mode="normal" in2="effect1_dropShadow_1049_9431" result="effect2_dropShadow_1049_9431"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_1049_9431" result="shape"/> +</filter> +<filter id="filter1_dd_1049_9431" x="15.1562" y="17.1458" width="11.6875" height="11.6875" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="0.229167"/> +<feGaussianBlur stdDeviation="0.229167"/> +<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.6 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1049_9431"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="0.458333"/> +<feGaussianBlur stdDeviation="0.6875"/> +<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.6 0"/> +<feBlend mode="normal" in2="effect1_dropShadow_1049_9431" result="effect2_dropShadow_1049_9431"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_1049_9431" result="shape"/> +</filter> +<filter id="filter2_dd_1049_9431" x="13.1704" y="8.89583" width="15.6594" height="6.18774" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="-0.916667"/> +<feGaussianBlur stdDeviation="0.229167"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1049_9431"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dy="-0.916667"/> +<feGaussianBlur stdDeviation="0.6875"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/> +<feBlend mode="normal" in2="effect1_dropShadow_1049_9431" result="effect2_dropShadow_1049_9431"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_1049_9431" result="shape"/> +</filter> +<linearGradient id="paint0_linear_1049_9431" x1="15.1875" y1="10.5078" x2="19.6603" y2="31.0495" gradientUnits="userSpaceOnUse"> +<stop offset="0.270833" stop-color="white"/> +<stop offset="1" stop-color="#DFDFDF"/> +</linearGradient> +<linearGradient id="paint1_linear_1049_9431" x1="21" y1="13.25" x2="25.0962" y2="32.4541" gradientUnits="userSpaceOnUse"> +<stop stop-color="#0078D4"/> +<stop offset="1" stop-color="#114A8B"/> +</linearGradient> +<linearGradient id="paint2_linear_1049_9431" x1="21" y1="11.1875" x2="21.1934" y2="14.1494" gradientUnits="userSpaceOnUse"> +<stop stop-color="#28AFEA"/> +<stop offset="1" stop-color="#0078D4"/> +</linearGradient> +<linearGradient id="paint3_linear_1049_9431" x1="21" y1="11.1875" x2="21.1934" y2="14.1494" gradientUnits="userSpaceOnUse"> +<stop stop-color="#30DAFF"/> +<stop offset="1" stop-color="#0094D4"/> +</linearGradient> +<linearGradient id="paint4_linear_1049_9431" x1="20.5035" y1="10.5" x2="20.5035" y2="11.875" gradientUnits="userSpaceOnUse"> +<stop stop-color="#22BCFF"/> +<stop offset="1" stop-color="#0088F0"/> +</linearGradient> +<linearGradient id="paint5_linear_1049_9431" x1="20.5035" y1="10.5" x2="20.5035" y2="11.875" gradientUnits="userSpaceOnUse"> +<stop stop-color="#28AFEA"/> +<stop offset="1" stop-color="#3CCBF4"/> +</linearGradient> +<clipPath id="clip0_1049_9431"> +<rect width="22" height="22" fill="white" transform="translate(10 10.5)"/> +</clipPath> +<clipPath id="clip1_1049_9431"> +<rect width="22" height="22" fill="white" transform="translate(10 10.5)"/> +</clipPath> +</defs> +</svg> diff --git a/docs/images/endless_game.png b/docs/images/endless_game.png new file mode 100644 index 0000000..8c8851b Binary files /dev/null and b/docs/images/endless_game.png differ diff --git a/docs/images/og_thumbnail.png b/docs/images/og_thumbnail.png new file mode 100644 index 0000000..4a574ff Binary files /dev/null and b/docs/images/og_thumbnail.png differ diff --git a/docs/images/overview.png b/docs/images/overview.png new file mode 100644 index 0000000..b97223a Binary files /dev/null and b/docs/images/overview.png differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..b3232eb --- /dev/null +++ b/docs/index.html @@ -0,0 +1,32 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>RunCat 365 + + + + + + + + + + + + + +
+ + + diff --git a/docs/privacy_policy.html b/docs/privacy_policy.html new file mode 100644 index 0000000..96e1dde --- /dev/null +++ b/docs/privacy_policy.html @@ -0,0 +1,22 @@ + + + + + + RunCat 365 — Privacy Policy + + + + + + +
+ + + diff --git a/docs/privacy_policy.ja.md b/docs/privacy_policy.ja.md new file mode 100644 index 0000000..d5af974 --- /dev/null +++ b/docs/privacy_policy.ja.md @@ -0,0 +1,46 @@ +:::header + +# RunCat 365 プライバシーポリシー + +[← RunCat 365 に戻る](./index.html?lang=ja) +::: + +中村 拓人(以下、開発者)は、お客様およびユーザーのプライバシーを重視しています。開発者はプライバシーが重要な問題であると認識しており、あなたのプライバシー保護を念頭に置いてサービスを設計・運営しています。このプライバシーポリシーは、RunCat 365 において開発者がどのような情報を収集・利用するか、またあなたの情報の安全性について説明するものです。RunCat 365 を利用することで、あなたはこのプライバシーポリシーに同意したものとみなされます。 + +## 1. 総則 + +### 1.1. このプライバシーポリシーの適用範囲 + +このプライバシーポリシーは、すべてのバージョンの RunCat 365 で提供されるすべてのサービスに適用されます。開発者は、サービスごとに個別のプライバシーポリシーまたは利用規約(個別契約)を定めることがあり、個別契約で情報の取り扱いを別途定めている場合は、個別契約の定めが優先されます。 + +なお、RunCat 365 に表示されたリンクから外部ウェブサイト等へ移動した場合の、リンク先における情報の取り扱いには、このプライバシーポリシーは適用されません。 + +### 1.2. 個人情報 + +個人情報には、あなたの氏名、住所、社会保障番号、メールアドレス、地理的位置情報が含まれます。 + +## 2. 開発者によるユーザー情報の取り扱い + +### 2.1. 決済に必要な情報 + +追加機能の購入など一部の購入サービスでは、決済のためにユーザーのクレジットカード情報を利用することがあります。この決済情報は、第三者が管理する決済プラットフォーム側に保存され、開発者が支払い情報を取得することはありません。 + +### 2.2. その他の情報 + +開発者は、RunCat 365 を通じてその他の個人情報を収集・利用することはありません。どうぞ安心して RunCat 365 をご利用ください。 + +## 3. このプライバシーポリシーの変更 + +このプライバシーポリシーは、随時変更されることがあります。開発者がこのプライバシーポリシーを変更する場合は、状況に応じて適切な方法で変更や更新を通知します。変更の掲載後も RunCat 365 を継続して利用した場合、あなたはその変更に同意したものとみなされます。 + +## 4. 開発者への連絡方法 + +このプライバシーポリシーまたは RunCat 365 に関するご質問、ご意見、その他のお問い合わせは、GitHub Issues を通じて開発者までお寄せください。 + +:::footer +[プライバシーポリシー](./privacy_policy.html?lang=ja) · [GitHub](https://github.com/runcat-dev/RunCat365) · [RunCat Developers](https://runcat-dev.github.io) + +[English](./privacy_policy.html) · **日本語** + +© 2022 Takuto Nakamura (Kyome22) +::: diff --git a/docs/privacy_policy.md b/docs/privacy_policy.md new file mode 100644 index 0000000..b7393dd --- /dev/null +++ b/docs/privacy_policy.md @@ -0,0 +1,46 @@ +:::header + +# RunCat 365 Privacy Policy + +[← Back to RunCat 365](./index.html) +::: + +Takuto Nakamura (hereinafter referred to as the developer) takes the privacy of his customers and users seriously. The developer recognizes that privacy is an important issue, so he designs and operates his services with the protection of your privacy in mind. This privacy policy explains what kind of information the developer collects, uses, and the security of your information in RunCat 365. By using RunCat 365, you consent to this privacy policy. + +## 1. General rules + +### 1.1. Scope of this privacy policy + +This privacy policy applies to all services provided in all versions of RunCat 365. The developer may separately set a privacy policy or terms of use (individual contracts) for each service, and in the case of specifying the handling of information separately in the individual contracts, the provision of the individual contracts will prevail. + +Please note that this privacy policy does not apply to the handling of information at the link destination when moving from a link displayed on RunCat 365 to an external website or the like. + +### 1.2. Personal information + +Personal information includes your name, physical address, social numbers, email address and geographic location information. + +## 2. How the developer deals with the user's information + +### 2.1. Information necessary for settlement + +Some purchase services, such as the purchase of additional functions, may use credit card information of the user for settlement. This settlement information is stored on the side of the settlement platform managed by a third party, and the developer will not acquire payment information. + +### 2.2. Other information + +The developer does not collect and use any other personal information through RunCat 365. Please use RunCat 365 with confidence. + +## 3. Changes to this privacy policy + +This privacy policy may change from time to time. If the developer decides to change this privacy policy, he will provide you additional forms of notice of modifications or updates as appropriate under the circumstances. Your continued use of RunCat 365 following the posting of changes will mean you accept those changes. + +## 4. How to contact the developer + +If you have any questions, comments, or other inquiries regarding this Privacy Policy or RunCat 365, please submit them to the developer via GitHub Issues. + +:::footer +[Privacy Policy](./privacy_policy.html) · [GitHub](https://github.com/runcat-dev/RunCat365) · [RunCat Developers](https://runcat-dev.github.io) + +**English** · [日本語](./privacy_policy.html?lang=ja) + +© 2022 Takuto Nakamura (Kyome22) +::: diff --git a/docs/style.css b/docs/style.css new file mode 100644 index 0000000..576dc26 --- /dev/null +++ b/docs/style.css @@ -0,0 +1,456 @@ +:root { + --lbs-bg: #f5f5f5; + --lbs-surface: #ffffff; + --lbs-ink: #2c2f36; + --lbs-ink-soft: #4a4f5a; + --lbs-muted: #606060; + --lbs-line: #e2e2e2; + --lbs-accent: #5f9ea0; + --lbs-accent-strong: #008b8b; + --lbs-accent-soft: #e3eeef; + --lbs-code-bg: #ededed; + --lbs-radius: 14px; + --lbs-shadow: 0 1px 2px rgba(44, 47, 54, 0.04), 0 8px 24px rgba(44, 47, 54, 0.06); + --lbs-shadow-strong: 0 2px 4px rgba(44, 47, 54, 0.05), 0 14px 32px rgba(44, 47, 54, 0.1); + --lbs-font-sans: "Exo 2", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, "Helvetica Neue", Arial, sans-serif; + --lbs-font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + margin: 0; + background: var(--lbs-bg); + color: var(--lbs-ink); + font-family: var(--lbs-font-sans); + line-height: 1.7; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +#content { + max-width: 880px; + margin: 0 auto; + padding: 3.5rem 1.5rem 4rem; +} + +/* ----- Header ----- */ + +.lbs-header { + padding-bottom: 2rem; + margin-bottom: 2.5rem; + border-bottom: 1px solid var(--lbs-line); + text-align: center; +} + +.lbs-header .lbs-heading-1 { + margin: 0; + font-style: italic; + font-size: clamp(2.75rem, 8vw, 4.5rem); + line-height: 1.05; + letter-spacing: -0.02em; + font-weight: 700; +} + +.lbs-header .lbs-paragraph { + margin: 0.85rem 0 0; + color: var(--lbs-ink-soft); + font-weight: 600; + font-size: 1.15rem; +} + +.lbs-header .lbs-paragraph a { + border-bottom: none; +} + +/* ----- Hero (lead, demo, download) ----- */ + +.lbs-header + .lbs-paragraph { + max-width: 36rem; + margin: 0 auto 2rem; + text-align: center; + font-size: 1.1rem; + color: var(--lbs-ink-soft); +} + +.lbs-image { + display: block; + max-width: 100%; + height: auto; + margin: 0 auto; +} + +.lbs-paragraph:has(.lbs-image) { + text-align: center; + margin: 1.5rem 0; +} + +.lbs-image[src*="demo"] { + width: 600px; + border-radius: var(--lbs-radius); + box-shadow: var(--lbs-shadow-strong); +} + +/* ----- Screenshots ----- */ + +.lbs-image[src*="overview"], +.lbs-image[src*="custom_runner"], +.lbs-image[src*="endless_game"] { + width: 100%; + max-width: 600px; + border-radius: var(--lbs-radius); + box-shadow: var(--lbs-shadow); +} + +/* ----- Microsoft Store badge ----- */ + +.lbs-image[src*="download_from_microsoft_store"] { + height: 60px; + width: auto; + transition: opacity 0.15s ease, transform 0.15s ease; +} + +.lbs-paragraph:has(.lbs-image[src*="download_from_microsoft_store"]) { + margin-top: 2rem; + margin-bottom: 0.5rem; +} + +.lbs-paragraph a:active .lbs-image[src*="download_from_microsoft_store"] { + opacity: 0.7; + transform: translateY(2px); +} + +/* availability line directly under the store badge */ +.lbs-paragraph:has(.lbs-image[src*="download_from_microsoft_store"]) + .lbs-paragraph { + text-align: center; + color: var(--lbs-muted); + font-size: 0.9rem; +} + +/* ----- Headings ----- */ + +.lbs-heading-2 { + margin: 3.5rem 0 1rem; + font-size: 1.65rem; + letter-spacing: -0.015em; + font-weight: 700; + scroll-margin-top: 1.5rem; +} + +.lbs-heading-3 { + margin: 0 0 0.5rem; + font-size: 1.15rem; + letter-spacing: -0.005em; + font-weight: 600; + color: var(--lbs-ink); +} + +/* ----- Body text ----- */ + +.lbs-paragraph { + margin: 0.85rem 0; + color: var(--lbs-ink-soft); +} + +.lbs-strong { + color: var(--lbs-ink); + font-weight: 600; +} + +.lbs-emphasis { + font-style: italic; +} + +.lbs-ul, +.lbs-ol { + padding-left: 1.4rem; + margin: 1rem 0; + color: var(--lbs-ink-soft); +} + +.lbs-list-item { + margin: 0.45rem 0; +} + +.lbs-list-item::marker { + color: var(--lbs-accent); +} + +/* ----- Links ----- */ + +.lbs-paragraph a, +.lbs-list-item a, +.lbs-details a { + color: var(--lbs-accent-strong); + text-decoration: none; + border-bottom: 1px solid rgba(0, 139, 139, 0.25); + transition: color 0.15s ease, border-color 0.15s ease; +} + +.lbs-paragraph a:hover, +.lbs-list-item a:hover, +.lbs-details a:hover { + color: var(--lbs-accent); + border-bottom-color: var(--lbs-accent); +} + +/* ----- Inline code ----- */ + +.lbs-code-span { + font-family: var(--lbs-font-mono); + font-size: 0.88em; + background: var(--lbs-code-bg); + color: var(--lbs-accent-strong); + padding: 0.12em 0.45em; + border-radius: 6px; +} + +/* ----- Feature cards (silent table / Warp) ----- */ + +.lbs-table.lbs-table-silent { + display: block; + margin: 1.5rem 0; + border: 0; +} + +.lbs-table-silent thead, +.lbs-table-silent tbody { + display: block; +} + +.lbs-table-silent tr { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + margin-bottom: 1rem; +} + +.lbs-table-silent tr:has(th:nth-child(3)) { + grid-template-columns: repeat(3, 1fr); +} + +.lbs-table-silent tr:last-child { + margin-bottom: 0; +} + +.lbs-table-silent th { + display: block; + background: var(--lbs-surface); + border: 1px solid var(--lbs-line); + border-radius: var(--lbs-radius); + padding: 1.5rem; + text-align: left; + font-weight: 400; + box-shadow: var(--lbs-shadow); + transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease; +} + +.lbs-table-silent th:hover { + transform: translateY(-3px); + box-shadow: var(--lbs-shadow-strong); + border-color: var(--lbs-accent); +} + +.lbs-table-silent th .lbs-heading-3 { + margin: 0 0 0.5rem; +} + +.lbs-table-silent th .lbs-paragraph { + margin: 0; + font-size: 0.96rem; +} + +/* ----- Image + list columns (monitor section) ----- */ + +.lbs-table-silent tr:has(.lbs-image) { + grid-template-columns: 1fr 1fr; + gap: 2.5rem; + align-items: start; +} + +.lbs-table-silent tr:has(.lbs-image) th { + background: none; + border: 0; + border-radius: 0; + padding: 0; + box-shadow: none; +} + +.lbs-table-silent tr:has(.lbs-image) th:hover { + transform: none; + box-shadow: none; + border-color: transparent; +} + +.lbs-table-silent tr:has(.lbs-image) .lbs-paragraph { + font-size: 1rem; + margin: 0 0 1rem; +} + +.lbs-table-silent tr:has(.lbs-image) .lbs-ul { + margin: 0; +} + +/* ----- Blockquote ----- */ + +.lbs-blockquote { + margin: 1.5rem 0; + padding: 1.1rem 1.4rem; + background: var(--lbs-surface); + border: 1px solid var(--lbs-line); + border-left: 4px solid var(--lbs-accent); + border-radius: 10px; + box-shadow: var(--lbs-shadow); +} + +.lbs-blockquote .lbs-paragraph { + margin: 0; + color: var(--lbs-ink); +} + +/* ----- Details / FAQ ----- */ + +.lbs-details { + background: var(--lbs-surface); + border: 1px solid var(--lbs-line); + border-radius: 12px; + padding: 0.25rem 1.1rem; + margin: 0.65rem 0; + transition: border-color 0.15s ease, box-shadow 0.15s ease; +} + +.lbs-details:hover { + border-color: rgba(95, 158, 160, 0.5); +} + +.lbs-details[open] { + border-color: var(--lbs-accent); + box-shadow: 0 6px 18px rgba(95, 158, 160, 0.12); +} + +.lbs-summary { + cursor: pointer; + padding: 0.85rem 1.75rem 0.85rem 0; + font-weight: 600; + color: var(--lbs-ink); + list-style: none; + position: relative; +} + +.lbs-summary::-webkit-details-marker { + display: none; +} + +.lbs-summary::after { + content: "+"; + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + font-size: 1.4rem; + line-height: 1; + color: var(--lbs-accent); + transition: transform 0.2s ease; +} + +.lbs-details[open] .lbs-summary::after { + content: "−"; +} + +.lbs-details .lbs-paragraph { + margin: 0 0 0.9rem; +} + +/* ----- Footer ----- */ + +.lbs-footer { + margin-top: 4rem; + padding-top: 1.5rem; + border-top: 1px solid var(--lbs-line); + color: var(--lbs-muted); + font-size: 0.9rem; + text-align: center; +} + +.lbs-footer .lbs-paragraph { + margin: 0.35rem 0; + color: var(--lbs-muted); +} + +.lbs-footer a { + color: var(--lbs-muted); + text-decoration: none; + border-bottom: 1px solid transparent; + transition: color 0.15s ease, border-color 0.15s ease; +} + +.lbs-footer a:hover { + color: var(--lbs-accent-strong); + border-bottom-color: var(--lbs-accent-strong); +} + +/* ----- Language switcher (footer) ----- */ + +.lbs-footer .lbs-strong { + color: var(--lbs-accent-strong); + font-weight: 700; +} + +/* ----- Legal pages (restrained, document-like header) ----- */ + +.privacy-policy .lbs-header { + text-align: left; +} + +.privacy-policy .lbs-header .lbs-heading-1 { + font-size: 1.9rem; + font-style: normal; + font-weight: 700; + letter-spacing: -0.01em; +} + +.privacy-policy .lbs-header .lbs-paragraph { + font-size: 0.95rem; + font-weight: 400; +} + +.privacy-policy .lbs-header + .lbs-paragraph { + max-width: none; + margin: 0.85rem 0; + text-align: left; + font-size: 1rem; +} + +/* ----- Responsive ----- */ + +@media (max-width: 640px) { + #content { + padding: 2.5rem 1.25rem 3rem; + } + + .lbs-image[src*="demo"] { + width: 100%; + } + + .lbs-table-silent tr, + .lbs-table-silent tr:has(th:nth-child(3)), + .lbs-table-silent tr:has(.lbs-image) { + grid-template-columns: 1fr; + } + + .lbs-table-silent tr:has(.lbs-image) { + gap: 1.5rem; + } + + .lbs-heading-2 { + margin-top: 2.75rem; + } +}