chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:26:32 +08:00
commit a4dc407056
177 changed files with 11414 additions and 0 deletions
+101
View File
@@ -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: <language name or BCP 47 code>
---
# 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 `<resheader>` blocks verbatim from `Strings.es.resx`
- Include every key from `Strings.resx` — no more, no less — with each `<value>` translated
- Keep all attribute names, XML structure, and `<comment>` content in English; translate only `<value>` 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
+113
View File
@@ -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
<concise summary of what this PR proposes — 13 sentences>
## Reason for the new feature
<If Bug Fix / Refactoring / Others: write "N/A">
<If New Feature: explain why the feature is necessary, how many users it benefits,
and why benefits outweigh maintenance cost>
## 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 "<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.
+8
View File
@@ -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
+67
View File
@@ -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
@@ -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
+42
View File
@@ -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
+28
View File
@@ -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.
+25
View File
@@ -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/
+52
View File
@@ -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`.
+189
View File
@@ -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 😸
+176
View File
@@ -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
+60
View File
@@ -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>
+7
View File
@@ -0,0 +1,7 @@
# WeHub 来源说明
- 原始项目:`runcat-dev/RunCat365`
- 原始仓库:https://github.com/runcat-dev/RunCat365
- 导入方式:上游默认分支的最新快照
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
+57
View File
@@ -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
+30
View File
@@ -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>
+25
View File
@@ -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>
+44
View File
@@ -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),
};
}
}
}
+103
View File
@@ -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);
}
}
}
+32
View File
@@ -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]);
}
}
}
+183
View File
@@ -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();
}
}
}
+119
View File
@@ -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
}
}
}
}
+508
View File
@@ -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);
}
}
+40
View File
@@ -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);
}
}
}
}
+900
View File
@@ -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);
}
}
}
}
+27
View File
@@ -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; } = [];
}
}
+233
View File
@@ -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();
}
}
}
+110
View File
@@ -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]);
}
}
}
+318
View File
@@ -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();
}
}
}
+120
View File
@@ -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>
+76
View File
@@ -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;
}
}
}
}
+179
View File
@@ -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;
}
}
}
+123
View File
@@ -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);
}
}
}
+122
View File
@@ -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();
}
}
}
+23
View File
@@ -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
}
}
+130
View File
@@ -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;
}
}
}
+137
View File
@@ -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;
}
}
}
}
+94
View File
@@ -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);
}
}
+122
View File
@@ -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;
}
}
}
+404
View File
@@ -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);
}
}
}
+483
View File
@@ -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));
}
}
}
}
+247
View File
@@ -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>
+531
View File
@@ -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);
}
}
}
}
+303
View File
@@ -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>
+303
View File
@@ -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>
+303
View File
@@ -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>
+303
View File
@@ -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>
+303
View File
@@ -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>
+303
View File
@@ -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>
+303
View File
@@ -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>
+303
View File
@@ -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>
+122
View File
@@ -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;
}
}
}
}
@@ -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>
+39
View File
@@ -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",
_ => "",
};
}
}
}
+77
View File
@@ -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>
+61
View File
@@ -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,
};
}
}
}
+62
View File
@@ -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;
}
}
}
}
+129
View File
@@ -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;
}
}
}
+100
View File
@@ -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,
};
}
}
}
+130
View File
@@ -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();
}
}
}
+61
View File
@@ -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;
}
}
}
}
+49
View File
@@ -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,
};
}
}
}
+44
View File
@@ -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}";
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Some files were not shown because too many files have changed in this diff Show More