code:
defmodule Qtfile.RoomsTest do
  use Qtfile.DataCase

  alias Qtfile.Rooms

  describe "rooms" do
    alias Qtfile.Rooms.Room

    @valid_attrs %{disabled: true, file_ttl: 42, files: "some files", motd: "some motd", owner: "some owner", room_id: "some room_id", room_name: "some room_name"}
    @update_attrs %{disabled: false, file_ttl: 43, files: "some updated files", motd: "some updated motd", owner: "some updated owner", room_id: "some updated room_id", room_name: "some updated room_name"}
    @invalid_attrs %{disabled: nil, file_ttl: nil, files: nil, motd: nil, owner: nil, room_id: nil, room_name: nil}

    def room_fixture(attrs \\ %{}) do
      {:ok, room} =
        attrs
        |> Enum.into(@valid_attrs)
        |> Rooms.create_room()

      room
    end

    test "list_rooms/0 returns all rooms" do
      room = room_fixture()
      assert Rooms.list_rooms() == [room]
    end

    test "get_room!/1 returns the room with given id" do
      room = room_fixture()
      assert Rooms.get_room!(room.id) == room
    end

    test "create_room/1 with valid data creates a room" do
      assert {:ok, %Room{} = room} = Rooms.create_room(@valid_attrs)
      assert room.disabled == true
      assert room.file_ttl == 42
      assert room.files == "some files"
      assert room.motd == "some motd"
      assert room.owner == "some owner"
      assert room.room_id == "some room_id"
      assert room.room_name == "some room_name"
    end

    test "create_room/1 with invalid data returns error changeset" do
      assert {:error, %Ecto.Changeset{}} = Rooms.create_room(@invalid_attrs)
    end

    test "update_room/2 with valid data updates the room" do
      room = room_fixture()
      assert {:ok, room} = Rooms.update_room(room, @update_attrs)
      assert %Room{} = room
      assert room.disabled == false
      assert room.file_ttl == 43
      assert room.files == "some updated files"
      assert room.motd == "some updated motd"
      assert room.owner == "some updated owner"
      assert room.room_id == "some updated room_id"
      assert room.room_name == "some updated room_name"
    end

    test "update_room/2 with invalid data returns error changeset" do
      room = room_fixture()
      assert {:error, %Ecto.Changeset{}} = Rooms.update_room(room, @invalid_attrs)
      assert room == Rooms.get_room!(room.id)
    end

    test "delete_room/1 deletes the room" do
      room = room_fixture()
      assert {:ok, %Room{}} = Rooms.delete_room(room)
      assert_raise Ecto.NoResultsError, fn -> Rooms.get_room!(room.id) end
    end

    test "change_room/1 returns a room changeset" do
      room = room_fixture()
      assert %Ecto.Changeset{} = Rooms.change_room(room)
    end
  end
end


code:
defmodule Qtfile.SingleTokenTest do
  use Qtfile.DataCase

  alias Qtfile.SingleToken

  describe "tokens" do
    alias Qtfile.SingleToken.Token

    @valid_attrs %{token: "some token"}
    @update_attrs %{token: "some updated token"}
    @invalid_attrs %{token: nil}

    def token_fixture(attrs \\ %{}) do
      {:ok, token} =
        attrs
        |> Enum.into(@valid_attrs)
        |> SingleToken.create_token()

      token
    end

    test "list_tokens/0 returns all tokens" do
      token = token_fixture()
      assert SingleToken.list_tokens() == [token]
    end

    test "get_token!/1 returns the token with given id" do
      token = token_fixture()
      assert SingleToken.get_token!(token.id) == token
    end

    test "create_token/1 with valid data creates a token" do
      assert {:ok, %Token{} = token} = SingleToken.create_token(@valid_attrs)
      assert token.token == "some token"
    end

    test "create_token/1 with invalid data returns error changeset" do
      assert {:error, %Ecto.Changeset{}} = SingleToken.create_token(@invalid_attrs)
    end

    test "update_token/2 with valid data updates the token" do
      token = token_fixture()
      assert {:ok, token} = SingleToken.update_token(token, @update_attrs)
      assert %Token{} = token
      assert token.token == "some updated token"
    end

    test "update_token/2 with invalid data returns error changeset" do
      token = token_fixture()
      assert {:error, %Ecto.Changeset{}} = SingleToken.update_token(token, @invalid_attrs)
      assert token == SingleToken.get_token!(token.id)
    end

    test "delete_token/1 deletes the token" do
      token = token_fixture()
      assert {:ok, %Token{}} = SingleToken.delete_token(token)
      assert_raise Ecto.NoResultsError, fn -> SingleToken.get_token!(token.id) end
    end

    test "change_token/1 returns a token changeset" do
      token = token_fixture()
      assert %Ecto.Changeset{} = SingleToken.change_token(token)
    end
  end
end

repo_name: Loves/qtfile

license: mit


code:
defmodule QtfileWeb.PageControllerTest do
  use QtfileWeb.ConnCase

  test "GET /", %{conn: conn} do
    conn = get conn, "/"
    assert html_response(conn, 200) =~ "Welcome to Phoenix!"
  end
end


code:
defmodule QtfileWeb.ErrorViewTest do
  use QtfileWeb.ConnCase, async: true

  # Bring render/3 and render_to_string/3 for testing custom views
  import Phoenix.View

  test "renders 404.html" do
    assert render_to_string(QtfileWeb.ErrorView, "404.html", []) ==
           "Page not found"
  end

  test "render 500.html" do
    assert render_to_string(QtfileWeb.ErrorView, "500.html", []) ==
           "Internal server error"
  end

  test "render any other" do
    assert render_to_string(QtfileWeb.ErrorView, "505.html", []) ==
           "Internal server error"
  end
end


code:
defmodule QtfileWeb.LayoutViewTest do
  use QtfileWeb.ConnCase, async: true
end


code:
defmodule QtfileWeb.PageViewTest do
  use QtfileWeb.ConnCase, async: true
end


code:
defmodule QtfileWeb.ChannelCase do
  @moduledoc """
  This module defines the test case to be used by
  channel tests.

  Such tests rely on `Phoenix.ChannelTest` and also
  import other functionality to make it easier
  to build common datastructures and query the data layer.

  Finally, if the test case interacts with the database,
  it cannot be async. For this reason, every test runs
  inside a transaction which is reset at the beginning
  of the test unless the test case is marked as async.
  """

  use ExUnit.CaseTemplate

  using do
    quote do
      # Import conveniences for testing with channels
      use Phoenix.ChannelTest

      # The default endpoint for testing
      @endpoint QtfileWeb.Endpoint
    end
  end

  setup tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Qtfile.Repo)
    unless tags[:async] do
      Ecto.Adapters.SQL.Sandbox.mode(Qtfile.Repo, {:shared, self()})
    end
    :ok
  end

end


code:
defmodule QtfileWeb.ConnCase do
  @moduledoc """
  This module defines the test case to be used by
  tests that require setting up a connection.

  Such tests rely on `Phoenix.ConnTest` and also
  import other functionality to make it easier
  to build common datastructures and query the data layer.

  Finally, if the test case interacts with the database,
  it cannot be async. For this reason, every test runs
  inside a transaction which is reset at the beginning
  of the test unless the test case is marked as async.
  """

  use ExUnit.CaseTemplate

  using do
    quote do
      # Import conveniences for testing with connections
      use Phoenix.ConnTest
      import QtfileWeb.Router.Helpers

      # The default endpoint for testing
      @endpoint QtfileWeb.Endpoint
    end
  end

  setup tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Qtfile.Repo)
    unless tags[:async] do
      Ecto.Adapters.SQL.Sandbox.mode(Qtfile.Repo, {:shared, self()})
    end
    {:ok, conn: Phoenix.ConnTest.build_conn()}
  end

end


code:
defmodule Qtfile.DataCase do
  @moduledoc """
  This module defines the setup for tests requiring
  access to the application's data layer.

  You may define functions here to be used as helpers in
  your tests.

  Finally, if the test case interacts with the database,
  it cannot be async. For this reason, every test runs
  inside a transaction which is reset at the beginning
  of the test unless the test case is marked as async.
  """

  use ExUnit.CaseTemplate

  using do
    quote do
      alias Qtfile.Repo

      import Ecto
      import Ecto.Changeset
      import Ecto.Query
      import Qtfile.DataCase
    end
  end

  setup tags do
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Qtfile.Repo)

    unless tags[:async] do
      Ecto.Adapters.SQL.Sandbox.mode(Qtfile.Repo, {:shared, self()})
    end

    :ok
  end

  @doc """
  A helper that transform changeset errors to a map of messages.

      assert {:error, changeset} = Accounts.create_user(%{password: "short"})
      assert "password is too short" in errors_on(changeset).password
      assert %{password: ["password is too short"]} = errors_on(changeset)

  """
  def errors_on(changeset) do
    Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
      Enum.reduce(opts, message, fn {key, value}, acc ->
        String.replace(acc, "%{#{key}}", to_string(value))
      end)
    end)
  end
end


code:
ExUnit.start()

Ecto.Adapters.SQL.Sandbox.mode(Qtfile.Repo, :manual)


code:
---
Language:        Cpp
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands:   true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
  AfterCaseLabel:  true
  AfterClass:      true
  AfterControlStatement: true
  AfterEnum:       true
  AfterFunction:   true
  AfterNamespace:  false
  AfterObjCDeclaration: true
  AfterStruct:     true
  AfterUnion:      true
  BeforeCatch:     true
  BeforeElse:      true
  IndentBraces:    false
  SplitEmptyFunction: true
  SplitEmptyRecord: true
  SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit:     120
CommentPragmas:  '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat:   false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
  - foreach
  - Q_FOREACH
  - BOOST_FOREACH
IncludeCategories:
  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
    Priority:        2
  - Regex:           '^(<|"(gtest|gmock|isl|json)/)'
    Priority:        3
  - Regex:           '.*'
    Priority:        1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: true
IndentWidth:     2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd:   ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments:  true
SortIncludes:    true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles:  false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard:        Cpp11
TabWidth:        2
UseTab:          Never
...
---
Language:        ObjC
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands:   true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
  AfterCaseLabel:  true
  AfterClass:      true
  AfterControlStatement: true
  AfterEnum:       true
  AfterFunction:   true
  AfterNamespace:  false
  AfterObjCDeclaration: true
  AfterStruct:     true
  AfterUnion:      true
  BeforeCatch:     true
  BeforeElse:      true
  IndentBraces:    false
  SplitEmptyFunction: true
  SplitEmptyRecord: true
  SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit:     120
CommentPragmas:  '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat:   false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
  - foreach
  - Q_FOREACH
  - BOOST_FOREACH
IncludeCategories:
  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
    Priority:        2
  - Regex:           '^(<|"(gtest|gmock|isl|json)/)'
    Priority:        3
  - Regex:           '.*'
    Priority:        1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: true
IndentWidth:     2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd:   ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
ReflowComments:  true
SortIncludes:    true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles:  false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard:        Cpp11
TabWidth:        2
UseTab:          Never
...

path: .clang-format


code:
*.c text
*.cpp text
*.h text
*.hpp text
*.hlsl text
*.xml text

*.sln text eol=crlf
*.vcxproj text eol=crlf
*.vcxproj.filter text eol=crlf

*.sh text eol=lf
CMakeLists.txt text eol=lf

path: .gitattributes


code:
name: Bug report
description: Report a bug in DuckStation
body:

- type: markdown
  attributes:
    value: |
      **Please read before creating a new bug report:**
      1. Make sure there is not already an issue for this bug by [searching in **both** open and closed issues](https://github.com/stenzek/duckstation/issues?q=is%3Aissue+sort%3Aupdated-desc+).
      2. All enhancements **must** be off. To quickly disable all enhancements with affecting your normal configuration, you can check **Disable All Enhancements** in Advanced Options.
      3. All advanced options **must** be at their default values.
      4. No cheats may be active. If you were using cheats, they should be disabled, and the game must be rebooted before reporting the bug.
      5. Do not share save state files. (Sharing memory card files is OK.)
      6. Verify your BIOS and game dumps, as we can not assist with issues resulting from bad dumps.
      7. If playing PAL region software, please check whether or not the game has LibCrypt protection. If it does, make sure you have a [correct SBI file](https://github.com/stenzek/duckstation#libcrypt-protection-and-sbi-files).
      8. Please post your bug report in English, as this is the only language spoken by the developers. The [Discord server](https://discord.gg/Buktv3t) has many helpful people if you need help translating.
      9. Issues about the libretro core will be deleted (you will be blocked from the repository if you create such an issue). That core is not DuckStation, it is a broken fork, and has nothing to do with us.

- type: input
  attributes:
    label: Game details
    description: |
      Specify the game's serial code, full name and region (USA, Europe, Japan).
    placeholder: SLUS-00404 Ace Combat 2 (USA)
  validations:
    required: true

- type: textarea
  attributes:
    label: Description of the issue/bug
    description: |
      Describe what you are seeing and/or hearing during gameplay. What doesn't work, and how do you expect it to work instead?
      You can include images or videos with drag and drop, and format code blocks or logs with <code>```</code> tags.
  validations:
    required: true

- type: textarea
  attributes:
    label: Steps to reproduce
    description: |
      Try to provide as much detail as possible to reproduce the issue.
      Having reproducible issues is a *prerequisite* for contributors to be able to solve them.
  validations:
    required: true

- type: input
  attributes:
    label: Software and hardware information
    description: |
      For desktops and laptops, specify your OS version, CPU and graphics card information (model and driver version).
      For mobile devices, specify your OS version and device model name.
    placeholder: Windows 10, Intel Core i7-7500U, Intel HD Graphics 620 (27.20.100.9616)
  validations:
    required: true

- type: input
  attributes:
    label: DuckStation version
    description: |
      Specify your DuckStation version and how you installed it (GitHub Releases, GitHub Actions, compiled from source, …).
  validations:
    required: true

- type: dropdown
  attributes:
    label: DuckStation rendering backend
    description: |
      Specify the DuckStation rendering backend you were using when reporting this issue.
      If you can reproduce this issue using more than one rendering backend, mention it in the **Description of the issue/bug** section above.
      When reporting a graphics-related issue, please test all the rendering backends you can before submitting the issue.
    options:
      - Software
      - Direct3D 11
      - Direct3D 12
      - OpenGL
      - Vulkan
  validations:
    required: true

- type: input
  attributes:
    label: DuckStation controller backend, drivers and wrappers
    description: |
      Which controller backend are you using in DuckStation's General Settings?
      Have you installed any drivers or wrappers on your system, or do you have any programs like Steam open?
      If so, specify which drivers/wrappers you are using.
  validations:
    required: true

- type: textarea
  attributes:
    label: Additional context
    description: |
      Add any other context about the problem here.


code:
blank_issues_enabled: false

contact_links:
  - name: Game compatibility list
    url: https://docs.google.com/spreadsheets/d/1H66MxViRjjE5f8hOl5RQmF5woS1murio2dsLn14kEqo/edit
    about: Please refer to the game compatibility list before opening an issue.

  - name: Discord server
  - url: https://discord.gg/Buktv3t
  - about: Please ask support questions on the Discord server, not here.


code:
name: Feature request
description: Request a feature to be added or improved in DuckStation
body:

- type: markdown
  attributes:
    value: |
      **Please read before creating a new feature request:**
      1. Make sure there is not already an issue for this feature request by [searching in **both** open and closed issues](https://github.com/stenzek/duckstation/issues?q=is%3Aissue+sort%3Aupdated-desc+).
      2. Please open **one issue per requested feature**. Do not cram several unrelated feature requests in a single issue, as this makes it harder for contributors to track what's being worked on.
      3. Please post your feature request in English, as this is the only language spoken by the developers. The [Discord server](https://discord.gg/Buktv3t) has many helpful people if you need help translating.
      4. Issues about the libretro core will be deleted (you will be blocked from the repository if you create such an issue). That core is not DuckStation, it is a broken fork, and has nothing to do with us.

- type: textarea
  attributes:
    label: Problem statement
    description: |
      Is your feature request related to a problem? Please describe.
    placeholder: Example - "I'm always frustrated when […]"
  validations:
    required: true

- type: textarea
  attributes:
    label: Proposed solution
    description: |
      A clear and concise description of what you want to happen.
  validations:
    required: true

- type: textarea
  attributes:
    label: Alternatives considered
    description: |
      Describe alternatives you've considered.
  validations:
    required: true

- type: textarea
  attributes:
    label: Additional context
    description: |
      Add any other context about the problem or proposed feature here.


code:
name: GameDB Lint

on:
  pull_request:
    paths:
      - 'data/resources/gamedb.yaml'
      - 'data/resources/discdb.yaml'
  push:
    branches:
      - master
      - dev
    paths:
      - 'data/resources/gamedb.yaml'
      - 'data/resources/discdb.yaml'
  workflow_dispatch:

jobs:
  gamedb-lint:
    runs-on: ubuntu-22.04
    timeout-minutes: 120
    steps:
    - uses: actions/checkout@v4.1.1
      with:
        fetch-depth: 0

    - name: Install Packages
      shell: bash
      run: |
        sudo apt-get update
        sudo apt-get -y install yamllint

    - name: Check GameDB
      shell: bash
      run: yamllint -c extras/yamllint-config.yaml -s -f github data/resources/gamedb.yaml

    - name: Check DiscDB
      shell: bash
      run: yamllint -c extras/yamllint-config.yaml -s -f github data/resources/discdb.yaml


code:
name: Create rolling release

on:
  pull_request:
    paths-ignore:
      - '**.md'
      - 'appveyor.yml'
      - 'scripts/*'
      - '.github/ISSUE_TEMPLATE/*'
  push:
    branches:
      - master
      - dev
    paths-ignore:
      - '**.md'
      - 'appveyor.yml'
      - 'scripts/*'
      - '.github/ISSUE_TEMPLATE/*'
  workflow_dispatch:

jobs:
  windows-build:
    runs-on: windows-2022
    timeout-minutes: 120
    steps:
    - uses: actions/checkout@v4.1.1
      with:
        fetch-depth: 0

    - name: Cache Dependencies
      id: cache-deps
      uses: actions/cache@v3.3.2
      with:
        path: |
          dep/msvc/deps-arm64
          dep/msvc/deps-x64
        key: deps ${{ hashFiles('scripts/build-dependencies-windows-arm64.bat', 'scripts/build-dependencies-windows-x64.bat') }}

    - name: Build X64 Dependencies
      if: steps.cache-deps.outputs.cache-hit != 'true'
      env:
        DEBUG: 0
      run: scripts/build-dependencies-windows-x64.bat

    - name: Build ARM64 Dependencies
      if: steps.cache-deps.outputs.cache-hit != 'true'
      env:
        DEBUG: 0
      run: scripts/build-dependencies-windows-arm64.bat

    - name: Tag as preview build
      if: github.ref == 'refs/heads/master'
      shell: cmd
      run: |
        echo #pragma once > src/scmversion/tag.h
        echo #define SCM_RELEASE_ASSET "duckstation-windows-x64-release.zip" >> src/scmversion/tag.h
        echo #define SCM_RELEASE_TAGS {"latest", "preview"} >> src/scmversion/tag.h
        echo #define SCM_RELEASE_TAG "preview" >> src/scmversion/tag.h

    - name: Tag as dev build
      if: github.ref == 'refs/heads/dev'
      shell: cmd
      run: |
        echo #pragma once > src/scmversion/tag.h
        echo #define SCM_RELEASE_ASSET "duckstation-windows-x64-release.zip" >> src/scmversion/tag.h
        echo #define SCM_RELEASE_TAGS {"latest", "preview"} >> src/scmversion/tag.h
        echo #define SCM_RELEASE_TAG "latest" >> src/scmversion/tag.h

    - name: Update RC version fields
      shell: cmd
      run: |
        cd src\scmversion
        call update_rc_version.bat
        cd ..\..
        git update-index --assume-unchanged src/duckstation-qt/duckstation-qt.rc
        git update-index --assume-unchanged src/duckstation-nogui/duckstation-nogui.rc

    - name: Compile x64 release build
      shell: cmd
      run: |
        call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
        msbuild duckstation.sln -t:Build -p:Platform=x64;Configuration=ReleaseLTCG-Clang

    - name: Create x64 symbols archive
      shell: cmd
      run: |
        "C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-x64-release-symbols.zip ./bin/x64/*.pdb

    - name: Upload x64 release symbols artifact
      uses: actions/upload-artifact@v1
      with:
        name: "windows"
        path: "duckstation-windows-x64-release-symbols.zip"

    - name: Remove extra bloat before archiving
      shell: cmd
      run: |
        del /Q bin\x64\*.pdb bin\x64\*.exp bin\x64\*.lib bin\x64\*.iobj bin\x64\*.ipdb bin\x64\common-tests*
        rename bin\x64\updater-x64-ReleaseLTCG.exe updater.exe

    - name: Create x64 release archive
      shell: cmd
      run: |
        "C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-x64-release.zip ./bin/x64/*

    - name: Upload x64 release artifact
      uses: actions/upload-artifact@v1
      with:
        name: "windows"
        path: "duckstation-windows-x64-release.zip"

  windows-arm64-build:
    runs-on: windows-2022
    timeout-minutes: 120
    steps:
    - uses: actions/checkout@v4.1.1
      with:
        fetch-depth: 0
        submodules: true

    - name: Cache Dependencies
      id: cache-deps
      uses: actions/cache@v3.3.2
      with:
        path: |
          dep/msvc/deps-arm64
          dep/msvc/deps-x64
        key: deps ${{ hashFiles('scripts/build-dependencies-windows-arm64.bat', 'scripts/build-dependencies-windows-x64.bat') }}

    - name: Build X64 Dependencies
      if: steps.cache-deps.outputs.cache-hit != 'true'
      env:
        DEBUG: 0
      run: scripts/build-dependencies-windows-x64.bat

    - name: Build ARM64 Dependencies
      if: steps.cache-deps.outputs.cache-hit != 'true'
      env:
        DEBUG: 0
      run: scripts/build-dependencies-windows-arm64.bat

    - name: Tag as preview build
      if: github.ref == 'refs/heads/master'
      shell: cmd
      run: |
        echo #pragma once > src/scmversion/tag.h
        echo #define SCM_RELEASE_ASSET "duckstation-windows-arm64-release.zip" >> src/scmversion/tag.h
        echo #define SCM_RELEASE_TAGS {"latest", "preview"} >> src/scmversion/tag.h
        echo #define SCM_RELEASE_TAG "preview" >> src/scmversion/tag.h

    - name: Tag as dev build
      if: github.ref == 'refs/heads/dev'
      shell: cmd
      run: |
        echo #pragma once > src/scmversion/tag.h
        echo #define SCM_RELEASE_ASSET "duckstation-windows-arm64-release.zip" >> src/scmversion/tag.h
        echo #define SCM_RELEASE_TAGS {"latest", "preview"} >> src/scmversion/tag.h
        echo #define SCM_RELEASE_TAG "latest" >> src/scmversion/tag.h

    - name: Update RC version fields
      shell: cmd
      run: |
        cd src\scmversion
        call update_rc_version.bat
        cd ..\..
        git update-index --assume-unchanged src/duckstation-qt/duckstation-qt.rc
        git update-index --assume-unchanged src/duckstation-nogui/duckstation-nogui.rc

    - name: Compile arm64 release build
      shell: cmd
      run: |
        call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64
        msbuild duckstation.sln -t:Build -p:Platform=ARM64;Configuration=ReleaseLTCG-Clang

    - name: Create arm64 symbols archive
      shell: cmd
      run: |
        "C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-arm64-release-symbols.zip ./bin/ARM64/*.pdb

    - name: Upload arm64 release symbols artifact
      uses: actions/upload-artifact@v1
      with:
        name: "windows-arm64"
        path: "duckstation-windows-arm64-release-symbols.zip"

    - name: Remove extra bloat before archiving
      shell: cmd
      run: |
        del /Q bin\ARM64\*.pdb bin\ARM64\*.exp bin\ARM64\*.lib bin\ARM64\*.iobj bin\ARM64\*.ipdb bin\ARM64\common-tests*
        rename bin\ARM64\updater-ARM64-ReleaseLTCG.exe updater.exe

    - name: Create arm64 release archive
      shell: cmd
      run: |
        "C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-arm64-release.zip ./bin/ARM64/*

    - name: Upload arm64 release artifact
      uses: actions/upload-artifact@v1
      with:
        name: "windows-arm64"
        path: "duckstation-windows-arm64-release.zip"

  linux-build:
    runs-on: ubuntu-22.04
    timeout-minutes: 120
    steps:
    # Work around https://github.com/actions/runner-images/issues/8659
    - name: Remove GCC 13 from runner image
      shell: bash
      run: |
        sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list
        sudo apt-get update
        sudo apt-get install -y --allow-downgrades 'libc6=2.35-0ubuntu*' 'libc6-dev=2.35-0ubuntu*' libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04

    - uses: actions/checkout@v4.1.1
      with:
        fetch-depth: 0

    - name: Install packages
      shell: bash
      run: |
        # Workaround for https://github.com/actions/runner-images/issues/675
        scripts/retry.sh wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
        sudo scripts/retry.sh apt-add-repository -n 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main'

        sudo scripts/retry.sh apt-get update &&
        sudo scripts/retry.sh apt-get -y install \
          cmake ninja-build ccache libegl1-mesa-dev libevdev-dev libwayland-dev libwayland-egl-backend-dev libxrandr-dev libdbus-1-dev \
          extra-cmake-modules libcurl4-openssl-dev libssl-dev libasound2-dev libpulse-dev libx11-xcb-dev build-essential git libclang-dev \
          libclang-11-dev libclang-12-dev patchelf libglib2.0-dev libfontconfig1-dev libharfbuzz-dev libjpeg-dev libpng-dev libfreetype-dev \
          libinput-dev libxcb-*-dev libxkbcommon-dev libxkbcommon-x11-dev libxrender-dev libwayland-dev libgl1-mesa-dev libegl-dev \
          libwebp-dev libzstd-dev libegl1-mesa-dev libgl1-mesa-dev libssl-dev libx11-dev libx11-xcb-dev libfuse2 libudev-dev \
          llvm-16 lld-16 clang-16

    - name: Cache Dependencies
      id: cache-deps
      uses: actions/cache@v3.3.2
      with:
        path: ~/deps
        key: deps ${{ hashFiles('scripts/build-dependencies.sh') }}

    - name: Build Dependencies
      if: steps.cache-deps.outputs.cache-hit != 'true'
      run: scripts/build-dependencies.sh "$HOME/deps"

    - name: Tag as preview build
      if: github.ref == 'refs/heads/master'
      run: |
        echo '#pragma once' > src/scmversion/tag.h
        echo '#define SCM_RELEASE_ASSET "DuckStation-x64.AppImage"' >> src/scmversion/tag.h
        echo '#define SCM_RELEASE_TAGS {"latest", "preview"}' >> src/scmversion/tag.h
        echo '#define SCM_RELEASE_TAG "preview"' >> src/scmversion/tag.h

    - name: Tag as dev build
      if: github.ref == 'refs/heads/dev'
      run: |
        echo '#pragma once' > src/scmversion/tag.h
        echo '#define SCM_RELEASE_ASSET "DuckStation-x64.AppImage"' >> src/scmversion/tag.h
        echo '#define SCM_RELEASE_TAGS {"latest", "preview"}' >> src/scmversion/tag.h
        echo '#define SCM_RELEASE_TAG "latest"' >> src/scmversion/tag.h

    - name: Compile build
      shell: bash
      run: |
        mkdir build
        cd build
        cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -DCMAKE_PREFIX_PATH=$HOME/deps -DCMAKE_TOOLCHAIN_FILE=../scripts/clang-toolchain.cmake ..
        cmake --build . --parallel
        cd ..
        scripts/appimage/make-appimage.sh $(realpath .) $(realpath ./build) $HOME/deps DuckStation-x64

    - name: Upload Qt AppImage
      uses: actions/upload-artifact@v1
      with:
        name: "linux-x64-appimage-qt"
        path: "DuckStation-x64.AppImage"

  linux-flatpak-build:
    runs-on: ubuntu-22.04
    container:
      image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.6
      options: --privileged
    timeout-minutes: 120
    steps:
    - uses: actions/checkout@v3
      with:
        fetch-depth: 0
        set-safe-directory: ${{ env.GITHUB_WORKSPACE }}

    # Work around container ownership issue
    - name: Set Safe Directory
      shell: bash
      run: git config --global --add safe.directory "*"

    - name: Generate AppStream XML
      run: |
        scripts/generate-metainfo.sh scripts/flatpak
        cat scripts/flatpak/org.duckstation.DuckStation.metainfo.xml

    - name: Validate AppStream XML
      run: flatpak-builder-lint appstream scripts/flatpak/org.duckstation.DuckStation.metainfo.xml

    - name: Validate manifest
      run: flatpak-builder-lint manifest scripts/flatpak/org.duckstation.DuckStation.json

    - name: Build Flatpak
      uses: flathub-infra/flatpak-github-actions/flatpak-builder@23796715b3dfa4c86ddf50cf29c3cc8b3c82dca8
      with:
        bundle: duckstation-x64.flatpak
        upload-artifact: false
        manifest-path: scripts/flatpak/org.duckstation.DuckStation.json
        arch: x86_64
        build-bundle: true
        verbose: true
        #mirror-screenshots-url: https://dl.flathub.org/media
        #branch: ${{ inputs.branch }}
        branch: master
        cache: true
        restore-cache: true
        cache-key: flatpak-x64-${{ hashFiles('scripts/flatpak/**/*.json') }}

    # fails due to screenshots..
    #- name: Validate build
    #  run: |
    #    flatpak-builder-lint repo repo

    - name: Upload Flatpak
      uses: actions/upload-artifact@v1
      with:
        name: "linux-flatpak"
        path: "duckstation-x64.flatpak"

  macos-build:
    runs-on: macos-14
    timeout-minutes: 120
    steps:
    - uses: actions/checkout@v4.1.1
      with:
        fetch-depth: 0

    - name: Install packages
      shell: bash
      run: |
        brew install curl ninja

    - name: Cache Dependencies
      id: cache-deps-mac
      uses: actions/cache@v3.3.2
      with:
        path: ~/deps
        key: deps-mac ${{ hashFiles('scripts/build-dependencies-mac.sh') }}

    - name: Build Dependencies
      if: steps.cache-deps-mac.outputs.cache-hit != 'true'
      run: scripts/build-dependencies-mac.sh "$HOME/deps"

    - name: Tag as preview build
      if: github.ref == 'refs/heads/master'
      run: |
        echo '#pragma once' > src/scmversion/tag.h
        echo '#define SCM_RELEASE_ASSET "duckstation-mac-release.zip"' >> src/scmversion/tag.h
        echo '#define SCM_RELEASE_TAGS {"latest", "preview"}' >> src/scmversion/tag.h
        echo '#define SCM_RELEASE_TAG "preview"' >> src/scmversion/tag.h

    - name: Tag as dev build
      if: github.ref == 'refs/heads/dev'
      run: |
        echo '#pragma once' > src/scmversion/tag.h
        echo '#define SCM_RELEASE_ASSET "duckstation-mac-release.zip"' >> src/scmversion/tag.h
        echo '#define SCM_RELEASE_TAGS {"latest", "preview"}' >> src/scmversion/tag.h
        echo '#define SCM_RELEASE_TAG "latest"' >> src/scmversion/tag.h

    - name: Compile and zip .app
      shell: bash
      run: |
        mkdir build
        cd build
        export MACOSX_DEPLOYMENT_TARGET=11.0
        cmake -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -DCMAKE_BUILD_TYPE=Release -DENABLE_OPENGL=OFF -DCMAKE_PREFIX_PATH="$HOME/deps" -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON -G Ninja ..
        cmake --build . --parallel
        mv bin/DuckStation.app .
        codesign -s - --deep -f -v DuckStation.app
        zip -r duckstation-mac-release.zip DuckStation.app/

    - name: Upload macOS .app
      uses: actions/upload-artifact@v1
      with:
        name: "macos"
        path: "build/duckstation-mac-release.zip"

  create-release:
    needs: [windows-build, windows-arm64-build, linux-build, linux-flatpak-build, macos-build]
    runs-on: "ubuntu-22.04"
    if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev'
    steps:
      - name: Download Windows Artifacts
        uses: actions/download-artifact@v1
        with:
          name: "windows"

      - name: Download Windows ARM64 Artifact
        uses: actions/download-artifact@v1
        with:
          name: "windows-arm64"

      - name: Download Qt AppImage Artifact
        uses: actions/download-artifact@v1
        with:
          name: "linux-x64-appimage-qt"

      - name: Download Flatpak Artifact
        uses: actions/download-artifact@v1
        with:
          name: "linux-flatpak"

      - name: Download MacOS Artifact
        uses: actions/download-artifact@v1
        with:
          name: "macos"

      - name: Create preview release
        if: github.ref == 'refs/heads/master'
        uses: "marvinpinto/action-automatic-releases@latest"
        with:
          repo_token: "${{ secrets.GITHUB_TOKEN }}"
          automatic_release_tag: "preview"
          prerelease: true
          title: "Latest Preview Build"
          files: |
            windows/duckstation-windows-x64-release.zip
            windows/duckstation-windows-x64-release-symbols.zip
            windows-arm64/duckstation-windows-arm64-release.zip
            windows-arm64/duckstation-windows-arm64-release-symbols.zip
            linux-x64-appimage-qt/DuckStation-x64.AppImage
            linux-flatpak/duckstation-x64.flatpak
            macos/duckstation-mac-release.zip

      - name: Create dev release
        if: github.ref == 'refs/heads/dev'
        uses: "marvinpinto/action-automatic-releases@latest"
        with:
          repo_token: "${{ secrets.GITHUB_TOKEN }}"
          automatic_release_tag: "latest"
          prerelease: false
          title: "Latest Rolling Release"
          files: |
            windows/duckstation-windows-x64-release.zip
            windows/duckstation-windows-x64-release-symbols.zip
            windows-arm64/duckstation-windows-arm64-release.zip
            windows-arm64/duckstation-windows-arm64-release-symbols.zip
            linux-x64-appimage-qt/DuckStation-x64.AppImage
            linux-flatpak/duckstation-x64.flatpak
            macos/duckstation-mac-release.zip


code:
# osx annoyances
.DS_Store

# binaries folder
/bin/
/Build/
/build/
/build-*/

# vs stuff
.vs
ipch
ipch/*
*.opensdf
*.sdf
*.suo
*.vcxproj.user
*.VC.opendb
*.VC.db
/.vscode/

# cmake stuff
CMakeCache.txt
CMakeFiles
Makefile
cmake_install.cmake
install_manifest.txt

# unix intermediate files
config.h
*.o
*.a
*.so
*.dylib

# qt creator
CMakeLists.txt.user

# python bytecode
__pycache__

# other repos
/dep/mac
/android

# UWP crap
Generated Files
/packages

path: .gitignore


code:
cmake_minimum_required(VERSION 3.16)
project(duckstation C CXX)

# Policy settings.
cmake_policy(SET CMP0069 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)

message(STATUS "CMake Version: ${CMAKE_VERSION}")
message(STATUS "CMake System Name: ${CMAKE_SYSTEM_NAME}")
message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}")

# Pull in modules.
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMakeModules/")
include(DuckStationUtils)

# Platform detection.
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
  set(LINUX TRUE)
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
  set(FREEBSD TRUE)
endif()

# Renderer options.
option(ENABLE_OPENGL "Build with OpenGL renderer" ON)
option(ENABLE_VULKAN "Build with Vulkan renderer" ON)

# Global options.
if(NOT ANDROID)
  option(BUILD_NOGUI_FRONTEND "Build the NoGUI frontend" OFF)
  option(BUILD_QT_FRONTEND "Build the Qt frontend" ON)
  option(BUILD_REGTEST "Build regression test runner" OFF)
  option(BUILD_TESTS "Build unit tests" OFF)

  set(ENABLE_CUBEB ON)
  set(ENABLE_DISCORD_PRESENCE ON)
  set(ENABLE_SDL2 ON)

  if(LINUX OR FREEBSD)
    option(ENABLE_X11 "Support X11 window system" ON)
    option(ENABLE_WAYLAND "Support Wayland window system" ON)
  endif()
  if(APPLE)
    option(SKIP_POSTPROCESS_BUNDLE "Disable bundle post-processing, including Qt additions" OFF)
  endif()
endif()

# Everything except Windows/Mac use EGL.
if(ENABLE_OPENGL AND (LINUX OR FREEBSD OR ANDROID))
  set(ENABLE_EGL TRUE)
endif()

if(ENABLE_X11)
  find_package(X11 REQUIRED)
  if (NOT X11_Xrandr_FOUND)
    message(FATAL_ERROR "XRandR extension is required")
  endif()
endif()
if(ENABLE_WAYLAND)
  message(STATUS "Wayland support enabled")
endif()

# Set _DEBUG macro for Debug builds.
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")

# Release build optimizations for MSVC.
if(MSVC)
  add_definitions("/D_CRT_SECURE_NO_WARNINGS")
  foreach(config CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
    # Set warning level 3 instead of 4.
    string(REPLACE "/W3" "/W4" ${config} "${${config}}")

    # Enable intrinsic functions, disable minimal rebuild, UTF-8 source, set __cplusplus version.
    set(${config} "${${config}} /Oi /Gm- /utf-8 /Zc:__cplusplus")
  endforeach()

  # RelWithDebInfo is set to Ob1 instead of Ob2.
  string(REPLACE "/Ob1" "/Ob2" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
  string(REPLACE "/Ob1" "/Ob2" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")

  # Disable incremental linking in RelWithDebInfo.
  string(REPLACE "/INCREMENTAL" "/INCREMENTAL:NO" CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")

  # COMDAT folding/remove unused functions.
  set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /OPT:REF /OPT:ICF")
  set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /OPT:REF /OPT:ICF")
endif()

# Default symbol visibility to hidden, that way we don't go through the PLT for intra-library calls.
if(ANDROID)
  cmake_policy(SET CMP0063 NEW)
  set(CMAKE_POLICY_DEFAULT_CMP0063 NEW)
  set(CMAKE_C_VISIBILITY_PRESET hidden)
  set(CMAKE_CXX_VISIBILITY_PRESET hidden)
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-semantic-interposition")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-semantic-interposition")
endif()

# Warning disables.
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    include(CheckCXXFlag)
    check_cxx_flag(-Wall COMPILER_SUPPORTS_WALL)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-switch")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-switch")
    if(NOT ANDROID)
      check_cxx_flag(-Wno-class-memaccess COMPILER_SUPPORTS_MEMACCESS)
      check_cxx_flag(-Wno-invalid-offsetof COMPILER_SUPPORTS_OFFSETOF)
    endif()
endif()

# Detect processor type.
if(APPLE AND NOT "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "")
  # Universal binaries.
  if("x86_64" IN_LIST CMAKE_OSX_ARCHITECTURES)
    set(CPU_ARCH_X64 TRUE)
    message(STATUS "Building x86_64 MacOS binaries.")
  endif()
  if("arm64" IN_LIST CMAKE_OSX_ARCHITECTURES)
    set(CPU_ARCH_ARM64 TRUE)
    message(STATUS "Building ARM64 MacOS binaries.")
  endif()
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "amd64" OR
       "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64" OR "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64")
  if(CMAKE_SIZEOF_VOID_P EQUAL 8)
    set(CPU_ARCH_X64 TRUE)
    message(STATUS "Building x86_64 binaries.")
  else()
    # Cross-compiling 32-bit build. 32-bit hosts are not supported.
    set(CPU_ARCH_X86 TRUE)
    message(FATAL_ERROR "Building x86_32 binaries is not supported.")
  endif()
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i386" OR
       "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686")
  set(CPU_ARCH_X86 TRUE)
  message(FATAL_ERROR "Building x86_32 binaries is not supported.")
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "aarch64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm64")
  if(CMAKE_SIZEOF_VOID_P EQUAL 8) # Might have an A64 kernel, e.g. Raspbian.
    set(CPU_ARCH_ARM64 TRUE)
    message(STATUS "Building ARM64 binaries.")
  else()
    set(CPU_ARCH_ARM32 TRUE)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -marm -march=armv7-a")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -marm -march=armv7-a")
    message(STATUS "Building ARM32 binaries on ARM64.")
  endif()
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7-a" OR
       "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7l")
  set(CPU_ARCH_ARM32 TRUE)
  message(STATUS "Building ARM32 binaries.")
  if(ANDROID)
    # Force ARM mode, since apparently ANDROID_ARM_MODE isn't working..
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -marm")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -marm")
  else()
    # Enable NEON.
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -marm -march=armv7-a")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -marm -march=armv7-a")
  endif()
elseif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "riscv64")
  set(CPU_ARCH_RISCV64 TRUE)
  message(STATUS "Building RISC-V 64 binaries.")
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -finline-atomics")
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -finline-atomics")

  # Still need this, apparently.
  link_libraries("-latomic")

  if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
    # Frame pointers generate an annoying amount of code on leaf functions.
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fomit-frame-pointer")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer")
  endif()
else()
  message(FATAL_ERROR "Unknown system processor: ${CMAKE_SYSTEM_PROCESSOR}")
endif()

# Detect page size if needed.
detect_page_size()

# We don't need exceptions, disable them to save a bit of code size.
if(MSVC)
  string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
  string(REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")

  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_HAS_EXCEPTIONS=0 /permissive-")
else()
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
endif()

# Write binaries to a seperate directory.
if(WIN32)
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/${CPU_ARCH}")
else()
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
endif()

# Needed for Linux - put shared libraries in the binary directory.
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")

# Enable threads everywhere.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

# Enable large file support on Linux 32-bit platforms.
# Android is deliberately ommitted here as it didn't support 64-bit ops on files until Android 7/N.
if((LINUX OR FREEBSD) AND (CPU_ARCH_X86 OR CPU_ARCH_ARM32))
  add_definitions("-D_FILE_OFFSET_BITS=64")
endif()

if(BUILD_TESTS)
  enable_testing()
endif()

# Prevent fmt from being built with exceptions, or being thrown at call sites.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFMT_EXCEPTIONS=0")

# Use C++17 for building dependencies (some aren't C++20-aware, e.g. reshadefx).
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Recursively include the source tree.
include(DuckStationDependencies)
add_subdirectory(dep)

# Use C++20 for building the main libraries.
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(src)

if(ANDROID)
  add_subdirectory(android/app/src/cpp)
endif()


code:
# Borrowed from PCSX2.

if(APPLE)
  function(add_metal_sources target sources)
    if(CMAKE_GENERATOR MATCHES "Xcode")
      # If we're generating an xcode project, you can just add the shaders to the main pcsx2 target and xcode will deal with them properly
      # This will make sure xcode supplies code completion, etc (if you use a custom command, it won't)
      set_target_properties(${target} PROPERTIES
        XCODE_ATTRIBUTE_MTL_ENABLE_DEBUG_INFO INCLUDE_SOURCE
      )
      foreach(shader IN LISTS sources)
        target_sources(${target} PRIVATE ${shader})
        set_source_files_properties(${shader} PROPERTIES LANGUAGE METAL)
      endforeach()
    else()
      function(generateMetallib std triple outputName)
        set(MetalShaderOut)
        set(flags
          -ffast-math
          $<$<NOT:$<CONFIG:Release,MinSizeRel>>:-gline-tables-only>
          $<$<NOT:$<CONFIG:Release,MinSizeRel>>:-MO>
        )
        foreach(shader IN LISTS sources)
          file(RELATIVE_PATH relativeShader "${CMAKE_SOURCE_DIR}" "${shader}")
          set(shaderOut ${CMAKE_CURRENT_BINARY_DIR}/${outputName}/${relativeShader}.air)
          list(APPEND MetalShaderOut ${shaderOut})
          get_filename_component(shaderDir ${shaderOut} DIRECTORY)
          add_custom_command(OUTPUT ${shaderOut}
            COMMAND ${CMAKE_COMMAND} -E make_directory ${shaderDir}
            COMMAND xcrun metal ${flags} -std=${std} -target ${triple} -o ${shaderOut} -c ${shader}
            DEPENDS ${shader}
          )
          set(metallib ${CMAKE_CURRENT_BINARY_DIR}/${outputName}.metallib)
        endforeach()
        add_custom_command(OUTPUT ${metallib}
          COMMAND xcrun metallib -o ${metallib} ${MetalShaderOut}
          DEPENDS ${MetalShaderOut}
        )
        target_sources(${target} PRIVATE ${metallib})
        set_source_files_properties(${metallib} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
      endfunction()
      generateMetallib(macos-metal2.0 air64-apple-macos10.13 default)
      generateMetallib(macos-metal2.2 air64-apple-macos10.15 Metal22)
      generateMetallib(macos-metal2.3 air64-apple-macos11.0  Metal23)
    endif()
  endfunction()
endif()


code:
include(CheckCXXCompilerFlag)

function(check_cxx_flag flag var)
    CHECK_CXX_COMPILER_FLAG("-Werror ${flag}" ${var})
    if(${var})
       set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE)
    endif()
endfunction()


code:
function(copy_base_translations target)
  get_target_property(MOC_EXECUTABLE_LOCATION Qt6::moc IMPORTED_LOCATION)
  get_filename_component(QT_BINARY_DIRECTORY "${MOC_EXECUTABLE_LOCATION}" DIRECTORY)
  find_program(LCONVERT_EXE lconvert HINTS "${QT_BINARY_DIRECTORY}")
  set(BASE_TRANSLATIONS_DIR "${QT_BINARY_DIRECTORY}/../translations")

  if(NOT APPLE)
    add_custom_command(TARGET ${target} POST_BUILD
      COMMAND "${CMAKE_COMMAND}" -E make_directory "$<TARGET_FILE_DIR:${target}>/translations")
  endif()

  file(GLOB qmFiles "${BASE_TRANSLATIONS_DIR}/qt_*.qm")
  foreach(path IN LISTS qmFiles)
    get_filename_component(file ${path} NAME)

    # qt_help_<lang> just has to ruin everything.
    if(file MATCHES "qt_help_" OR NOT file MATCHES "qt_([^.]+).qm")
      continue()
    endif()

    # If qtbase_<lang>.qm exists, merge all qms for that language into a single qm.
    set(lang "${CMAKE_MATCH_1}")
    set(baseQmPath "${BASE_TRANSLATIONS_DIR}/qtbase_${lang}.qm")
    if(EXISTS "${baseQmPath}")
      set(outPath "${CMAKE_CURRENT_BINARY_DIR}/qt_${lang}.qm")
      set(srcQmFiles)
      file(GLOB langQmFiles "${BASE_TRANSLATIONS_DIR}/qt*${lang}.qm")
      foreach(qmFile IN LISTS langQmFiles)
        get_filename_component(file ${qmFile} NAME)
        if(file STREQUAL "qt_${lang}.qm")
          continue()
        endif()
        LIST(APPEND srcQmFiles "${qmFile}")
      endforeach()
      add_custom_command(OUTPUT ${outPath}
        COMMAND "${LCONVERT_EXE}" -verbose -of qm -o "${outPath}" ${srcQmFiles}
        DEPENDS ${srcQmFiles}
      )
      set(path "${outPath}")
    endif()

    target_sources(${target} PRIVATE ${path})
    if(APPLE)
      set_source_files_properties(${path} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/translations)
    else()
      add_custom_command(TARGET ${target} POST_BUILD
        COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${path}" "$<TARGET_FILE_DIR:${target}>/translations")
    endif()
  endforeach()
endfunction()


code:
if(ENABLE_SDL2)
  find_package(SDL2 2.30.2 REQUIRED)
endif()
if(NOT WIN32 AND NOT ANDROID)
  # From PCSX2: On macOS, Mono.framework contains an ancient version of libpng. We don't want that.
  # Avoid it by telling cmake to avoid finding frameworks while we search for libpng.
  if(APPLE)
    set(FIND_FRAMEWORK_BACKUP ${CMAKE_FIND_FRAMEWORK})
    set(CMAKE_FIND_FRAMEWORK NEVER)
  endif()

  find_package(Zstd 1.5.5 REQUIRED)
  find_package(WebP REQUIRED) # v1.3.2, spews an error on Linux because no pkg-config.
  find_package(ZLIB REQUIRED) # 1.3, but Mac currently doesn't use it.
  find_package(PNG 1.6.40 REQUIRED)
  find_package(JPEG REQUIRED) # No version because flatpak uses libjpeg-turbo.
  find_package(CURL REQUIRED)
  find_package(Freetype 2.13.1 REQUIRED)

  if(ENABLE_VULKAN OR APPLE)
    find_package(Shaderc REQUIRED)
  endif()

  if(APPLE)
    set(CMAKE_FIND_FRAMEWORK ${FIND_FRAMEWORK_BACKUP})
  endif()
endif()
if(LINUX AND NOT ANDROID)
  find_package(UDEV REQUIRED)
endif()
if(UNIX AND NOT APPLE)
  find_package(Libbacktrace)
  if(NOT LIBBACKTRACE_FOUND)
    message(WARNING "libbacktrace not found, crashes will not produce backtraces.")
  endif()
endif()


code:
function(disable_compiler_warnings_for_target target)
	if(MSVC)
		target_compile_options(${target} PRIVATE "/W0")
	else()
		target_compile_options(${target} PRIVATE "-w")
	endif()
endfunction()

function(detect_page_size)
  # This is only needed for ARM64, or if the user hasn't overridden it explicitly.
  if(NOT CPU_ARCH_ARM64 OR HOST_PAGE_SIZE)
    return()
  endif()

  if(NOT LINUX OR ANDROID)
    # For universal Apple builds, we use preprocessor macros to determine page size.
    # Similar for Windows, except it's always 4KB.
    return()
  endif()

  if(CMAKE_CROSSCOMPILING)
    message(WARNING "Cross-compiling and can't determine page size, assuming default.")
    return()
  endif()

  message(STATUS "Determining host page size")
  set(detect_page_size_file ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.c)
  file(WRITE ${detect_page_size_file} "
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
  int res = sysconf(_SC_PAGESIZE);
  printf(\"%d\", res);
  return (res > 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}")
  try_run(
    detect_page_size_run_result
    detect_page_size_compile_result
    ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}
    ${detect_page_size_file}
    RUN_OUTPUT_VARIABLE detect_page_size_output)
  if(NOT detect_page_size_compile_result OR NOT detect_page_size_run_result EQUAL 0)
    message(FATAL_ERROR "Could not determine host page size.")
  else()
    message(STATUS "Host page size: ${detect_page_size_output}")
    set(HOST_PAGE_SIZE ${detect_page_size_output} CACHE STRING "Reported host page size")
  endif()
endfunction()


code:
#.rst:
# ECMFindModuleHelpers
# --------------------
#
# Helper macros for find modules: ecm_find_package_version_check(),
# ecm_find_package_parse_components() and
# ecm_find_package_handle_library_components().
#
# ::
#
#   ecm_find_package_version_check(<name>)
#
# Prints warnings if the CMake version or the project's required CMake version
# is older than that required by extra-cmake-modules.
#
# ::
#
#   ecm_find_package_parse_components(<name>
#       RESULT_VAR <variable>
#       KNOWN_COMPONENTS <component1> [<component2> [...]]
#       [SKIP_DEPENDENCY_HANDLING])
#
# This macro will populate <variable> with a list of components found in
# <name>_FIND_COMPONENTS, after checking that all those components are in the
# list of KNOWN_COMPONENTS; if there are any unknown components, it will print
# an error or warning (depending on the value of <name>_FIND_REQUIRED) and call
# return().
#
# The order of components in <variable> is guaranteed to match the order they
# are listed in the KNOWN_COMPONENTS argument.
#
# If SKIP_DEPENDENCY_HANDLING is not set, for each component the variable
# <name>_<component>_component_deps will be checked for dependent components.
# If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
# dependencies will also be added to <variable>.
#
# ::
#
#   ecm_find_package_handle_library_components(<name>
#       COMPONENTS <component> [<component> [...]]
#       [SKIP_DEPENDENCY_HANDLING])
#       [SKIP_PKG_CONFIG])
#
# Creates an imported library target for each component.  The operation of this
# macro depends on the presence of a number of CMake variables.
#
# The <name>_<component>_lib variable should contain the name of this library,
# and <name>_<component>_header variable should contain the name of a header
# file associated with it (whatever relative path is normally passed to
# '#include'). <name>_<component>_header_subdir variable can be used to specify
# which subdirectory of the include path the headers will be found in.
# ecm_find_package_components() will then search for the library
# and include directory (creating appropriate cache variables) and create an
# imported library target named <name>::<component>.
#
# Additional variables can be used to provide additional information:
#
# If SKIP_PKG_CONFIG, the <name>_<component>_pkg_config variable is set, and
# pkg-config is found, the pkg-config module given by
# <name>_<component>_pkg_config will be searched for and used to help locate the
# library and header file.  It will also be used to set
# <name>_<component>_VERSION.
#
# Note that if version information is found via pkg-config,
# <name>_<component>_FIND_VERSION can be set to require a particular version
# for each component.
#
# If SKIP_DEPENDENCY_HANDLING is not set, the INTERFACE_LINK_LIBRARIES property
# of the imported target for <component> will be set to contain the imported
# targets for the components listed in <name>_<component>_component_deps.
# <component>_FOUND will also be set to false if any of the compoments in
# <name>_<component>_component_deps are not found.  This requires the components
# in <name>_<component>_component_deps to be listed before <component> in the
# COMPONENTS argument.
#
# The following variables will be set:
#
# ``<name>_TARGETS``
#   the imported targets
# ``<name>_LIBRARIES``
#   the found libraries
# ``<name>_INCLUDE_DIRS``
#   the combined required include directories for the components
# ``<name>_DEFINITIONS``
#   the "other" CFLAGS provided by pkg-config, if any
# ``<name>_VERSION``
#   the value of ``<name>_<component>_VERSION`` for the first component that
#   has this variable set (note that components are searched for in the order
#   they are passed to the macro), although if it is already set, it will not
#   be altered
#
# Note that these variables are never cleared, so if
# ecm_find_package_handle_library_components() is called multiple times with
# different components (typically because of multiple find_package() calls) then
# ``<name>_TARGETS``, for example, will contain all the targets found in any
# call (although no duplicates).
#
# Since pre-1.0.0.

#=============================================================================
# Copyright 2014 Alex Merry <alex.merry@kde.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

include(CMakeParseArguments)

macro(ecm_find_package_version_check module_name)
    if(CMAKE_VERSION VERSION_LESS 2.8.12)
        message(FATAL_ERROR "CMake 2.8.12 is required by Find${module_name}.cmake")
    endif()
    if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
        message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Find${module_name}.cmake")
    endif()
endmacro()

macro(ecm_find_package_parse_components module_name)
    set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING)
    set(ecm_fppc_oneValueArgs RESULT_VAR)
    set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS)
    cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN})

    if(ECM_FPPC_UNPARSED_ARGUMENTS)
        message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}")
    endif()
    if(NOT ECM_FPPC_RESULT_VAR)
        message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components")
    endif()
    if(NOT ECM_FPPC_KNOWN_COMPONENTS)
        message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components")
    endif()
    if(NOT ECM_FPPC_DEFAULT_COMPONENTS)
        set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS})
    endif()

    if(${module_name}_FIND_COMPONENTS)
        set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS})

        if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING)
            # Make sure deps are included
            foreach(ecm_fppc_comp ${ecm_fppc_requestedComps})
                foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps})
                    list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index)
                    if("${ecm_fppc_index}" STREQUAL "-1")
                        if(NOT ${module_name}_FIND_QUIETLY)
                            message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}")
                        endif()
                        list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}")
                    endif()
                endforeach()
            endforeach()
        else()
            message(STATUS "Skipping dependency handling for ${module_name}")
        endif()
        list(REMOVE_DUPLICATES ecm_fppc_requestedComps)

        # This makes sure components are listed in the same order as
        # KNOWN_COMPONENTS (potentially important for inter-dependencies)
        set(${ECM_FPPC_RESULT_VAR})
        foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS})
            list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index)
            if(NOT "${ecm_fppc_index}" STREQUAL "-1")
                list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}")
                list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index})
            endif()
        endforeach()
        # if there are any left, they are unknown components
        if(ecm_fppc_requestedComps)
            set(ecm_fppc_msgType STATUS)
            if(${module_name}_FIND_REQUIRED)
                set(ecm_fppc_msgType FATAL_ERROR)
            endif()
            if(NOT ${module_name}_FIND_QUIETLY)
                message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}")
            endif()
            return()
        endif()
    else()
        set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS})
    endif()
endmacro()

macro(ecm_find_package_handle_library_components module_name)
    set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING)
    set(ecm_fpwc_oneValueArgs)
    set(ecm_fpwc_multiValueArgs COMPONENTS)
    cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN})

    if(ECM_FPWC_UNPARSED_ARGUMENTS)
        message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}")
    endif()
    if(NOT ECM_FPWC_COMPONENTS)
        message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components")
    endif()

    include(FindPackageHandleStandardArgs)
    find_package(PkgConfig)
    foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS})
        set(ecm_fpwc_dep_vars)
        set(ecm_fpwc_dep_targets)
        if(NOT SKIP_DEPENDENCY_HANDLING)
            foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps})
                list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND")
                list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}")
            endforeach()
        endif()

        if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config)
            pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET
                              ${${module_name}_${ecm_fpwc_comp}_pkg_config})
        endif()

        find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
            NAMES ${${module_name}_${ecm_fpwc_comp}_header}
            HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS}
            PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir}
        )
        find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY
            NAMES ${${module_name}_${ecm_fpwc_comp}_lib}
            HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS}
        )

        set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}")
        if(NOT ${module_name}_VERSION)
            set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION})
        endif()

        find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp}
            FOUND_VAR
                ${module_name}_${ecm_fpwc_comp}_FOUND
            REQUIRED_VARS
                ${module_name}_${ecm_fpwc_comp}_LIBRARY
                ${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
                ${ecm_fpwc_dep_vars}
            VERSION_VAR
                ${module_name}_${ecm_fpwc_comp}_VERSION
            )

        mark_as_advanced(
            ${module_name}_${ecm_fpwc_comp}_LIBRARY
            ${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
        )

        if(${module_name}_${ecm_fpwc_comp}_FOUND)
            list(APPEND ${module_name}_LIBRARIES
                        "${${module_name}_${ecm_fpwc_comp}_LIBRARY}")
            list(APPEND ${module_name}_INCLUDE_DIRS
                        "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}")
            set(${module_name}_DEFINITIONS
                    ${${module_name}_DEFINITIONS}
                    ${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS})
            if(NOT TARGET ${module_name}::${ecm_fpwc_comp})
                add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED)
                set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES
                    IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}"
                    INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}"
                    INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}"
                    INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}"
                )
            endif()
            list(APPEND ${module_name}_TARGETS
                        "${module_name}::${ecm_fpwc_comp}")
        endif()
    endforeach()
    if(${module_name}_LIBRARIES)
        list(REMOVE_DUPLICATES ${module_name}_LIBRARIES)
    endif()
    if(${module_name}_INCLUDE_DIRS)
        list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS)
    endif()
    if(${module_name}_DEFINITIONS)
        list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS)
    endif()
    if(${module_name}_TARGETS)
        list(REMOVE_DUPLICATES ${module_name}_TARGETS)
    endif()
endmacro()


code:
# - Try to find libbacktrace
# Once done this will define
#  LIBBACKTRACE_FOUND - System has libbacktrace
#  LIBBACKTRACE_INCLUDE_DIRS - The libbacktrace include directories
#  LIBBACKTRACE_LIBRARIES - The libraries needed to use libbacktrace

FIND_PATH(
    LIBBACKTRACE_INCLUDE_DIR backtrace.h
    HINTS /usr/include /usr/local/include
    ${LIBBACKTRACE_PATH_INCLUDES}
)

FIND_LIBRARY(
    LIBBACKTRACE_LIBRARY
    NAMES backtrace
    PATHS ${ADDITIONAL_LIBRARY_PATHS} ${LIBBACKTRACE_PATH_LIB}
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Libbacktrace DEFAULT_MSG
                                  LIBBACKTRACE_LIBRARY LIBBACKTRACE_INCLUDE_DIR)

if(LIBBACKTRACE_FOUND)
    add_library(libbacktrace::libbacktrace UNKNOWN IMPORTED)
    set_target_properties(libbacktrace::libbacktrace PROPERTIES
        IMPORTED_LOCATION ${LIBBACKTRACE_LIBRARY}
        INTERFACE_INCLUDE_DIRECTORIES ${LIBBACKTRACE_INCLUDE_DIR}
    )
endif()

mark_as_advanced(LIBBACKTRACE_INCLUDE_DIR LIBBACKTRACE_LIBRARY)


code:
# - Try to find OpenSLES
# Once done this will define
#  OPENSLES_FOUND - System has OpenSLES
#  OPENSLES_INCLUDE_DIR - The OpenSLES include directory
#  OPENSLES_LIBRARY - The library needed to use OpenSLES
# An imported target OpenSLES::OpenSLES is also created, prefer this

find_path(OPENSLES_INCLUDE_DIR
  NAMES SLES/OpenSLES.h
)

find_library(OPENSLES_LIBRARY
  NAMES OpenSLES
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(OpenSLES  DEFAULT_MSG
                                  OPENSLES_LIBRARY OPENSLES_INCLUDE_DIR)

if(OpenSLES_FOUND)
  if(NOT TARGET OpenSLES::OpenSLES)
    add_library(OpenSLES::OpenSLES UNKNOWN IMPORTED)
    set_target_properties(OpenSLES::OpenSLES PROPERTIES
      IMPORTED_LOCATION ${OPENSLES_LIBRARY}
      INTERFACE_INCLUDE_DIRECTORIES ${OPENSLES_INCLUDE_DIR}
    )
  endif()
endif()

mark_as_advanced(OPENSLES_INCLUDE_DIR OPENSLES_LIBRARY )


code:
# - Try to find SHADERC
# Once done this will define
#  SHADERC_FOUND - System has SHADERC
#  SHADERC_INCLUDE_DIRS - The SHADERC include directories
#  SHADERC_LIBRARIES - The libraries needed to use SHADERC

FIND_PATH(
    SHADERC_INCLUDE_DIR shaderc/shaderc.h
    HINTS /usr/include /usr/local/include
    ${SHADERC_PATH_INCLUDES}
)

FIND_LIBRARY(
    SHADERC_LIBRARY
    NAMES shaderc_shared
    PATHS ${ADDITIONAL_LIBRARY_PATHS} ${SHADERC_PATH_LIB}
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Shaderc DEFAULT_MSG
                                  SHADERC_LIBRARY SHADERC_INCLUDE_DIR)

if(SHADERC_FOUND)
    add_library(Shaderc::shaderc_shared UNKNOWN IMPORTED)
    set_target_properties(Shaderc::shaderc_shared PROPERTIES
        IMPORTED_LOCATION ${SHADERC_LIBRARY}
        INTERFACE_INCLUDE_DIRECTORIES ${SHADERC_INCLUDE_DIR}
        INTERFACE_COMPILE_DEFINITIONS "SHADERC_SHAREDLIB"
    )
endif()

mark_as_advanced(SHADERC_INCLUDE_DIR SHADERC_LIBRARY)


code:
# - Try to find UDEV
# Once done, this will define
#
#  UDEV_FOUND - system has UDEV
#  UDEV_INCLUDE_DIRS - the UDEV include directories
#  UDEV_LIBRARIES - the UDEV library
find_package(PkgConfig)

pkg_check_modules(UDEV_PKGCONF libudev)

find_path(UDEV_INCLUDE_DIRS
  NAMES libudev.h
  PATHS ${UDEV_PKGCONF_INCLUDE_DIRS}
)

find_library(UDEV_LIBRARIES
  NAMES udev
  PATHS ${UDEV_PKGCONF_LIBRARY_DIRS}
)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(UDEV DEFAULT_MSG UDEV_INCLUDE_DIRS UDEV_LIBRARIES)

mark_as_advanced(UDEV_INCLUDE_DIRS UDEV_LIBRARIES)

if(UDEV_FOUND AND NOT (TARGET UDEV::UDEV))
  add_library (UDEV::UDEV UNKNOWN IMPORTED)
  set_target_properties(UDEV::UDEV
    PROPERTIES
    IMPORTED_LOCATION ${UDEV_LIBRARIES}
    INTERFACE_INCLUDE_DIRECTORIES ${UDEV_INCLUDE_DIRS})
endif()


code:
# Copyright (C) 2020 Sony Interactive Entertainment Inc.
# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
# Copyright (C) 2013 Igalia S.L.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1.  Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
# 2.  Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#[=======================================================================[.rst:
FindWebP
--------------

Find WebP headers and libraries.

Imported Targets
^^^^^^^^^^^^^^^^

``WebP::libwebp``
  The WebP library, if found.

``WebP::demux``
  The WebP demux library, if found.

Result Variables
^^^^^^^^^^^^^^^^

This will define the following variables in your project:

``WebP_FOUND``
  true if (the requested version of) WebP is available.
``WebP_VERSION``
  the version of WebP.
``WebP_LIBRARIES``
  the libraries to link against to use WebP.
``WebP_INCLUDE_DIRS``
  where to find the WebP headers.
``WebP_COMPILE_OPTIONS``
  this should be passed to target_compile_options(), if the
  target is not used for linking

#]=======================================================================]

find_package(PkgConfig QUIET)
pkg_check_modules(PC_WEBP QUIET libwebp)
set(WebP_COMPILE_OPTIONS ${PC_WEBP_CFLAGS_OTHER})
set(WebP_VERSION ${PC_WEBP_CFLAGS_VERSION})

find_path(WebP_INCLUDE_DIR
    NAMES webp/decode.h
    HINTS ${PC_WEBP_INCLUDEDIR} ${PC_WEBP_INCLUDE_DIRS}
)

find_library(WebP_LIBRARY
    NAMES ${WebP_NAMES} webp libwebp
    HINTS ${PC_WEBP_LIBDIR} ${PC_WEBP_LIBRARY_DIRS}
)

# There's nothing in the WebP headers that could be used to detect the exact
# WebP version being used so don't attempt to do so. A version can only be found
# through pkg-config
if ("${WebP_FIND_VERSION}" VERSION_GREATER "${WebP_VERSION}")
    if (WebP_VERSION)
        message(FATAL_ERROR "Required version (" ${WebP_FIND_VERSION} ") is higher than found version (" ${WebP_VERSION} ")")
    else ()
        message(WARNING "Cannot determine WebP version without pkg-config")
    endif ()
endif ()

# Find components
if (WebP_INCLUDE_DIR AND WebP_LIBRARY)
    set(_WebP_REQUIRED_LIBS_FOUND ON)
    set(WebP_LIBS_FOUND "WebP (required): ${WebP_LIBRARY}")
else ()
    set(_WebP_REQUIRED_LIBS_FOUND OFF)
    set(WebP_LIBS_NOT_FOUND "WebP (required)")
endif ()

if ("demux" IN_LIST WebP_FIND_COMPONENTS)
    find_library(WebP_DEMUX_LIBRARY
        NAMES ${WebP_DEMUX_NAMES} webpdemux libwebpdemux
        HINTS ${PC_WEBP_LIBDIR} ${PC_WEBP_LIBRARY_DIRS}
    )

    if (WebP_DEMUX_LIBRARY)
        if (WebP_FIND_REQUIRED_demux)
            list(APPEND WebP_LIBS_FOUND "demux (required): ${WebP_DEMUX_LIBRARY}")
        else ()
           list(APPEND WebP_LIBS_FOUND "demux (optional): ${WebP_DEMUX_LIBRARY}")
        endif ()
    else ()
        if (WebP_FIND_REQUIRED_demux)
           set(_WebP_REQUIRED_LIBS_FOUND OFF)
           list(APPEND WebP_LIBS_NOT_FOUND "demux (required)")
        else ()
           list(APPEND WebP_LIBS_NOT_FOUND "demux (optional)")
        endif ()
    endif ()
endif ()

if (NOT WebP_FIND_QUIETLY)
    if (WebP_LIBS_FOUND)
        message(STATUS "Found the following WebP libraries:")
        foreach (found ${WebP_LIBS_FOUND})
            message(STATUS " ${found}")
        endforeach ()
    endif ()
    if (WebP_LIBS_NOT_FOUND)
        message(STATUS "The following WebP libraries were not found:")
        foreach (found ${WebP_LIBS_NOT_FOUND})
            message(STATUS " ${found}")
        endforeach ()
    endif ()
endif ()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WebP
    FOUND_VAR WebP_FOUND
    REQUIRED_VARS WebP_INCLUDE_DIR WebP_LIBRARY _WebP_REQUIRED_LIBS_FOUND
    VERSION_VAR WebP_VERSION
)

if (WebP_LIBRARY AND NOT TARGET WebP::libwebp)
    add_library(WebP::libwebp UNKNOWN IMPORTED GLOBAL)
    set_target_properties(WebP::libwebp PROPERTIES
        IMPORTED_LOCATION "${WebP_LIBRARY}"
        INTERFACE_COMPILE_OPTIONS "${WebP_COMPILE_OPTIONS}"
        INTERFACE_INCLUDE_DIRECTORIES "${WebP_INCLUDE_DIR}"
    )
endif ()

if (WebP_DEMUX_LIBRARY AND NOT TARGET WebP::demux)
    add_library(WebP::demux UNKNOWN IMPORTED GLOBAL)
    set_target_properties(WebP::demux PROPERTIES
        IMPORTED_LOCATION "${WebP_DEMUX_LIBRARY}"
        INTERFACE_COMPILE_OPTIONS "${WebP_COMPILE_OPTIONS}"
        INTERFACE_INCLUDE_DIRECTORIES "${WebP_INCLUDE_DIR}"
    )
endif ()

mark_as_advanced(
    WebP_INCLUDE_DIR
    WebP_LIBRARY
    WebP_DEMUX_LIBRARY
)

if (WebP_FOUND)
    set(WebP_LIBRARIES ${WebP_LIBRARY} ${WebP_DEMUX_LIBRARY})
    set(WebP_INCLUDE_DIRS ${WebP_INCLUDE_DIR})
endif ()


code:
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# 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.

#
# - Try to find Facebook zstd library
# This will define
# Zstd_FOUND
# Zstd_INCLUDE_DIR
# Zstd_LIBRARY
#

find_path(Zstd_INCLUDE_DIR NAMES zstd.h)

find_library(Zstd_LIBRARY_DEBUG NAMES zstdd zstd_staticd)
find_library(Zstd_LIBRARY_RELEASE NAMES zstd zstd_static)

include(SelectLibraryConfigurations)
SELECT_LIBRARY_CONFIGURATIONS(Zstd)

include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
    Zstd DEFAULT_MSG
    Zstd_LIBRARY Zstd_INCLUDE_DIR
)

mark_as_advanced(Zstd_INCLUDE_DIR Zstd_LIBRARY)

if(Zstd_FOUND AND NOT (TARGET Zstd::Zstd))
  add_library (Zstd::Zstd UNKNOWN IMPORTED)
  set_target_properties(Zstd::Zstd
    PROPERTIES
    IMPORTED_LOCATION ${Zstd_LIBRARY}
    INTERFACE_INCLUDE_DIRECTORIES ${Zstd_INCLUDE_DIR})
endif()


code:
# DuckStation Contributors List

The following people have contributed to the project in some way, and are credited here.

## Code Contributions

- Connor McLaughlin - @stenzek
- @ggrtk
- @CookiePLMonster
- @PookaMustard

## Translators

- Anderson Cardoso - Portuguese (Br)
- @bajolzas - Portuguese (Pt)
- posix - @Richard-L, blexx - German
- @phoe-nix, @zkdpower - Chinese (Simplified)
- Sorer - @MojoJojoDojo - Hebrew
- Hipnosis - @Hipnosis183, MrHomunculus, @falsepopsky - Spanish
- @RaydenX93 - Italian
- @r57zone - Russian
- @6lackmag3 - Russian (Android)
- @DenSinH - Dutch
- @BenjaminSiskoo - French
- mikakunin - Japanese
- Zuzia, Seba, @CookiePLMonster - Polish
- Foxtrot Uniform - Turkish

## UI Contributions

- @kamfretoz
- @maxihplay (MBee)

## Cheat Database

- Pugsy
- Unicorngoulash

## Game Compatibility Database

- @Zet-sensei
- @DarkFrost89
- @macattack222
- @HeroponRikiBestest
- @picili
- @andercard0
- @Abbanon
- @Shideravan
- @mirrornoir
- @pryon
- @MojoJojoDojo
- @heckez-sys
- @Damaniel
- @RaydenX93
- @gp2man
- @Richard-L
- @pan2marumie3
- @CookiePLMonster
- @LoStraniero91
- @JFD62780
- @lmarciano9
- @Facepalm38
- @Alien-Grey
- @dmlipat
- @Krusher97
- @AngryScotsmanGaming
- @PookaMustard
- @waspennator
- @Serpentario
- @QuasarDGames
- @egamboau
- @goldstinger
- @DankRank
- @Kesnos-ho
- @Facepalm38
- @ZL1LAC
- @cs50-account
- @nxrighthere
- @Overload86
- @landcaster
- @Sekai9

## Special Thanks

The following people did not directly contribute to the emulator, but it would not be in the state if not for them.

- nocash (https://problemkaputt.de/) for fantastic documentation.
- @PeterLemon for great simple test programs.
- amidog for CPU, GTE and GPU test programs.
- Jakub Czekański - @JaCzekanski - for collaboration on hardware tests.


code:
# DuckStation - PlayStation 1, aka. PSX Emulator
[Features](#features) | [Downloading and Running](#downloading-and-running) | [Building](#building) | [Disclaimers](#disclaimers)

**Latest Builds for Windows 10/11 (x64/ARM64), Linux (AppImage/Flatpak), and macOS (11.0+ Universal):** https://github.com/stenzek/duckstation/releases/tag/latest

**Game Compatibility List:** https://docs.google.com/spreadsheets/d/e/2PACX-1vRE0jjiK_aldpICoy5kVQlpk2f81Vo6P4p9vfg4d7YoTOoDlH4PQHoXjTD2F7SdN8SSBLoEAItaIqQo/pubhtml

**Discord Server:** https://www.duckstation.org/discord.html

DuckStation is an simulator/emulator of the Sony PlayStation(TM) console, focusing on playability, speed, and long-term maintainability. The goal is to be as accurate as possible while maintaining performance suitable for low-end devices. "Hack" options are discouraged, the default configuration should support all playable games with only some of the enhancements having compatibility issues.

A "BIOS" ROM image is required to to start the emulator and to play games. You can use an image from any hardware version or region, although mismatching game regions and BIOS regions may have compatibility issues. A ROM image is not provided with the emulator for legal reasons, you should dump this from your own console using Caetla or other means.

## Features

DuckStation features a fully-featured frontend built using Qt, as well as a fullscreen/TV UI based on Dear ImGui.

Other features include:

 - CPU Recompiler/JIT (x86-64, armv7/AArch32, AArch64, RISC-V/RV64).
 - Hardware (D3D11, D3D12, OpenGL, Vulkan, Metal) and software rendering.
 - Upscaling, texture filtering, and true colour (24-bit) in hardware renderers.
 - PGXP for geometry precision, texture correction, and depth buffer emulation.
 - Adaptive downsampling filter.
 - Post processing shader chains (GLSL and experimental Reshade FX).
 - "Fast boot" for skipping BIOS splash/intro.
 - Save state support.
 - Windows, Linux, macOS support.
 - Supports bin/cue images, raw bin/img files, MAME CHD, single-track ECM, MDS/MDF, and unencrypted PBP formats.
 - Direct booting of homebrew executables.
 - Direct loading of Portable Sound Format (psf) files.
 - Digital and analog controllers for input (rumble is forwarded to host).
 - Namco GunCon lightgun support (simulated with mouse).
 - NeGcon support.
 - Qt and "Big Picture" UI.
 - Automatic updates with preview and latest channels.
 - Automatic content scanning - game titles/hashes are provided by redump.org.
 - Optional automatic switching of memory cards for each game.
 - Supports loading cheats from existing lists.
 - Memory card editor and save importer.
 - Emulated CPU overclocking.
 - Integrated and remote debugging.
 - Multitap controllers (up to 8 devices).
 - RetroAchievements.
 - Automatic loading/applying of PPF patches.

## System Requirements
 - A CPU faster than a potato. But it needs to be x86_64, AArch32/armv7, AArch64/ARMv8, or RISC-V/RV64.
 - For the hardware renderers, a GPU capable of OpenGL 3.1/OpenGL ES 3.1/Direct3D 11 Feature Level 10.0 (or Vulkan 1.0) and above. So, basically anything made in the last 10 years or so.
 - SDL, XInput or DInput compatible game controller (e.g. XB360/XBOne/XBSeries). DualShock 3 users on Windows will need to install the official DualShock 3 drivers included as part of PlayStation Now.

## Downloading and running
Binaries of DuckStation for Windows x64/ARM64, Linux x86_64 (in AppImage/Flatpak formats), and macOS Universal Binaries are available via GitHub Releases and are automatically built with every commit/push. Binaries or packages distributed through other sources may be out of date and are not supported by the developer, please speak to them for support, not us.

### Windows

DuckStation **requires** Windows 10/11, specifically version 1809 or newer. If you are still using Windows 7/8/8.1, DuckStation **will not run** on your operating system. Running these operating systems in 2023 should be considered a security risk, and I would recommend updating to something which receives vendor support.
If you must use an older operating system, [v0.1-5624](https://github.com/stenzek/duckstation/releases/tag/v0.1-5624) is the last version which will run. But do not expect to recieve any assistance, these builds are no longer supported.

To download:
 - Go to https://github.com/stenzek/duckstation/releases/tag/latest, and download the Windows x64 build. This is a zip archive containing the prebuilt binary.
 - Alternatively, direct download link: https://github.com/stenzek/duckstation/releases/download/latest/duckstation-windows-x64-release.zip
 - Extract the archive **to a subdirectory**. The archive has no root subdirectory, so extracting to the current directory will drop a bunch of files in your download directory if you do not extract to a subdirectory.

Once downloaded and extracted, you can launch the emulator with `duckstation-qt-x64-ReleaseLTCG.exe`. Follow the Setup Wizard to get started.

**If you get an error about `vcruntime140_1.dll` being missing, you will need to update your Visual C++ runtime.** You can do that from this page: https://support.microsoft.com/en-au/help/2977003/the-latest-supported-visual-c-downloads. Specifically, you want the x64 runtime, which can be downloaded from https://aka.ms/vs/17/release/vc_redist.x64.exe.

### Linux

The only supported version of DuckStation for Linux are the AppImage and Flatpak in the releases page. If you installed DuckStation from another source or distribution (e.g. EmuDeck), you should contact the packager for support, we have no control over it.

#### AppImage

The AppImages require a distribution equivalent to Ubuntu 22.04 or newer to run.

 - Go to https://github.com/stenzek/duckstation/releases/tag/latest, and download `duckstation-x64.AppImage`.
 - Run `chmod a+x` on the downloaded AppImage -- following this step, the AppImage can be run like a typical executable.

#### Flatpak

 - Go to https://github.com/stenzek/duckstation/releases/tag/latest, and download `duckstation-x64.flatpak`.
 - Run `flatpak install ./duckstation-x64.flatpak`.

or, if you have FlatHub set up:
 - Run `flatpak install org.duckstation.DuckStation`.

Use `flatpak run org.duckstation.DuckStation` to start, or select `DuckStation` in the launcher of your desktop environment. Follow the Setup Wizard to get started.

### macOS

Universal MacOS builds are provided for both x64 and ARM64 (Apple Silicon).

MacOS Big Sir (11.0) is required, as this is also the minimum requirement for Qt.

To download:
 - Go to https://github.com/stenzek/duckstation/releases/tag/latest, and download `duckstation-mac-release.zip`.
 - Extract the zip by double-clicking it.
 - Open DuckStation.app, optionally moving it to your desired location first.
 - Depending on GateKeeper configuration, you may need to right click -> Open the first time you run it, as code signing certificates are out of the question for a project which brings in zero revenue.

### Android

You will need a device with armv7 (32-bit ARM), AArch64 (64-bit ARM), or x86_64 (64-bit x86). 64-bit is preferred, the requirements are higher for 32-bit, you'll probably want at least a 1.5GHz CPU.

Download from Google Play: https://play.google.com/store/apps/details?id=com.github.stenzek.duckstation
APK and Beta Downloads: https://www.duckstation.org/android/

**No support is provided for the Android app**, it is free and your expectations should be in line with that. Please **do not** email me about issues about it, or ask for help, you will be ignored.

To use:
1. Install and run the app for the first time.
2. Follow the setup wizard.

If you have an external controller, you will need to map the buttons and sticks in settings.

### LibCrypt protection and SBI files

A number of PAL region games use LibCrypt protection, requiring additional CD subchannel information to run properly. libcrypt not functioning usually manifests as hanging or crashing, but can sometimes affect gameplay too, depending on how the game implemented it.

For these games, make sure that the CD image and its corresponding SBI (.sbi) file have the same name and are placed in the same directory. DuckStation will automatically load the SBI file when it is found next to the CD image.

For example, if your disc image was named `Spyro3.cue`, you would place the SBI file in the same directory, and name it `Spyro3.sbi`.

CHD images with built-in subchannel information are also supported.

## Building

### Windows
Requirements:
 - Visual Studio 2022

1. Clone the respository: `git clone https://github.com/stenzek/duckstation.git`.
2. Download the dependencies pack from https://github.com/stenzek/duckstation-ext-qt-minimal/releases/download/latest/deps-x64.7z, and extract it to `dep\msvc`.
3. Open the Visual Studio solution `duckstation.sln` in the root, or "Open Folder" for cmake build.
4. Build solution.
5. Binaries are located in `bin/x64`.
6. Run `duckstation-qt-x64-Release.exe` or whichever config you used.

### Linux
Requirements (Debian/Ubuntu package names):
 - CMake (`cmake`)
 - SDL2 (at least version 2.30.2) (`libsdl2-dev` `libxrandr-dev`)
 - pkgconfig (`pkg-config`)
 - Qt 6 (at least version 6.7.0) (`qt6-base-dev` `qt6-base-private-dev` `qt6-base-dev-tools` `qt6-tools-dev` `libqt6svg6`)
 - git (`git`) (Note: needed to clone the repository and at build time)
 - When Wayland is enabled (default): (`libwayland-dev` `libwayland-egl-backend-dev` `extra-cmake-modules` `qt6-wayland`)
 - libcurl (`libcurl4-openssl-dev`)
 - Optional for faster building: Ninja (`ninja-build`)

Due to Ubuntu LTS using older package versions, DuckStation releases use manually built dependency libraries. If you are using a distribution with older package versions, you can build the required library versions using the script found at https://github.com/stenzek/duckstation/blob/master/scripts/build-dependencies.sh.

Fedora package names (assumes you are using the dependency build script):
```
alsa-lib-devel brotli-devel clang cmake dbus-devel egl-wayland-devel fontconfig-devel gcc-c++ libcurl-devel libdecor-devel libevdev-devel libICE-devel libinput-devel libSM-devel libX11-devel libXau-devel libxcb-devel libXcomposite-devel libXcursor-devel libXext-devel libXfixes-devel libXft-devel libXi-devel libxkbcommon-devel libxkbcommon-x11-devel libXpresent-devel libXrandr-devel libXrender-devel lld llvm make mesa-libEGL-devel mesa-libGL-devel ninja-build openssl-devel patch pcre2-devel perl-Digest-SHA pipewire-devel pulseaudio-libs-devel systemd-devel wayland-devel xcb-util-cursor-devel xcb-util-devel xcb-util-errors-devel xcb-util-image-devel xcb-util-keysyms-devel xcb-util-renderutil-devel xcb-util-wm-devel xcb-util-xrm-devel zlib-devel
```

1. Clone the repository: `git clone https://github.com/stenzek/duckstation.git -b dev`.
2. Create a build directory, either in-tree or elsewhere.
3. Run CMake to configure the build system. Assuming a build subdirectory of `build-release`, run `cmake -Bbuild-release -DCMAKE_BUILD_TYPE=Release`. If you have installed Ninja, add `-GNinja` at the end of the CMake command line for faster builds. If you used the dependency build script, the path you built the dependencies in should be supplied with `-DCMAKE_PREFIX_PATH=/path/to/deps`.
4. Compile the source code. For the example above, run `cmake --build build-release --parallel`.
5. Run the binary, located in the build directory under `bin/duckstation-qt`.

### macOS

Requirements:
 - CMake
 - Xcode

1. Clone the repository: `git clone https://github.com/stenzek/duckstation.git`.
2. Build the dependencies. `scripts/build-dependencies-mac.sh /PATH/TO/SAVE/DEPS` (substitute with your own location).
2. Run CMake to configure the build system: `cmake -Bbuild-release -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/PATH/TO/SAVE/DEPS`.
4. Compile the source code: `cmake --build build-release --parallel`.
5. Run the binary, located in the build directory under `bin/DuckStation.app`.

## User Directories
The "User Directory" is where you should place your BIOS images, where settings are saved to, and memory cards/save states are saved by default.
An optional [SDL game controller database file](#sdl-game-controller-database) can be also placed here.

This is located in the following places depending on the platform you're using:

- Windows: My Documents\DuckStation
- Linux: `$XDG_DATA_HOME/duckstation`, or `~/.local/share/duckstation`.
- macOS: `~/Library/Application Support/DuckStation`.

So, if you were using Linux, you would place your BIOS images in `~/.local/share/duckstation/bios`. This directory will be created upon running DuckStation
for the first time.

If you wish to use a "portable" build, where the user directory is the same as where the executable is located, create an empty file named `portable.txt`
in the same directory as the DuckStation executable.

## Bindings for Qt frontend
Your keyboard or game controller can be used to simulate a variety of PlayStation controllers. Controller input is supported through DInput, XInput, and SDL backends and can be changed through `Settings -> General Settings`.

To bind your input device, go to `Settings -> Controllers`. Each of the buttons/axes for the simulated controller will be listed, alongside the corresponding key/button on your device that it is currently bound to. To rebind, click the box next to the button/axis name, and press the key or button on your input device that you wish to bind to. When binding rumble, simply press any button on the controller you wish to send rumble to.

## SDL Game Controller Database
DuckStation releases ship with a database of game controller mappings for the SDL controller backend, courtesy of https://github.com/gabomdq/SDL_GameControllerDB. The included `gamecontrollerdb.txt` file can be found in the `database` subdirectory of the DuckStation program directory.

If you are experiencing issues binding your controller with the SDL controller backend, you may need to add a custom mapping to the database file. Make a copy of `gamecontrollerdb.txt` and place it in your [user directory](#user-directories) (or directly in the program directory, if running in portable mode) and then follow the instructions in the [SDL_GameControllerDB repository](https://github.com/gabomdq/SDL_GameControllerDB) for creating a new mapping. Add this mapping to the new copy of `gamecontrollerdb.txt` and your controller should then be recognized properly.

## Default bindings
Controller 1:
 - **Left Stick:** W/A/S/D
 - **Right Stick:** T/F/G/H
 - **D-Pad:** Up/Left/Down/Right
 - **Triangle/Square/Circle/Cross:** I/J/L/K
 - **L1/R1:** Q/E
 - **L2/R2:** 1/3
 - **L3/R3:** 2/4
 - **Start:** Enter
 - **Select:** Backspace

Hotkeys:
 - **Escape:** Open Pause Menu
 - **F11:** Toggle Fullscreen
 - **Tab:** Temporarily Disable Speed Limiter
 - **Space:** Pause/Resume Emulation

## Disclaimers

Icon by icons8: https://icons8.com/icon/74847/platforms.undefined.short-title

"PlayStation" and "PSX" are registered trademarks of Sony Interactive Entertainment Europe Limited. This project is not affiliated in any way with Sony Interactive Entertainment.