Compare commits

..
1 Commits
Author SHA1 Message Date
Christian 2f7ad51a02 Fix the ghost tile entity issue @ the client. Closes #884
Fix stairs using fungible blocks
Fix fluid draining

Updated FML:
MinecraftForge/FML@b33e7808d8 Fix for new asset URLs
MinecraftForge/FML@82851384f1 Don't delete if md5 fails. Stops derpitude with new assets stuffs.
MinecraftForge/FML@3eb3c2700c Really fix asset and library downloading -.- Wish we could switch over to gradle and not have to go through this headake.
MinecraftForge/FML@f1b3381e61 Fix the "ordering" issue on windows. Windows is poop.
2013-12-18 00:26:02 -05:00
3255 changed files with 42176 additions and 176622 deletions

No files matched your search

+2 -18
View File
@@ -1,23 +1,7 @@
* text eol=lf
*.bat text eol=crlf
*.patch text eol=lf
*.java text eol=lf
*.gradle text eol=crlf
*.patch text
*.png binary
*.gif binary
*.exe binary
*.dll binary
*.jar binary
*.lzma binary
*.zip binary
*.pyd binary
*.cfg text eol=lf
*.py text eol=lf
*.jks binary
# Disable autocrlf on generated files, they always generate with LF
src/generated/**/.cache/cache text eol=lf
src/generated/**/*.json text eol=lf
src/generated_test/**/.cache/cache text eol=lf
src/generated_test/**/*.json text eol=lf
*.zip binary
-13
View File
@@ -1,13 +0,0 @@
---
name: Feature request
about: Want to request a feature?
title: ''
labels: Feature
assignees: ''
---
<!-- Please note, feature requests that are submitted as GitHub issues here are rarely implemented. -->
<!-- If you'd like to see something in Forge, you should be ready to implement it as a PR. -->
<!-- If you're not sure, open a Draft PR or discuss it in the Discord server. -->
<!-- Discord: https://discord.gg/UvedJ9m -->
-26
View File
@@ -1,26 +0,0 @@
---
name: Issue report
about: Visit the forum first for issues with crashes.
title: ''
labels: Triage
assignees: ''
---
<!-- Thank you for reporting! -->
<!-- For support with crashes and issues, please create a post on the forum instead: -->
<!-- https://www.minecraftforge.net/forum/forum/18-support-bug-reports/ -->
**Minecraft Version:** {Minecraft version}
**Forge Version:** {Forge version. *Version number, not latest/rb*}
**Logs:** {Link(s) to GitHub Gist with full latest.log and/or crash report}
**Steps to Reproduce:**
1. Do this
2. Then do that
3. ...
**Description of issue:**
-43
View File
@@ -1,43 +0,0 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Label to use when marking as stale
staleLabel: Stale
# Comment to post when removing the stale label. Set to `false` to disable
unmarkComment: false
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 1
# Issues with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
- Assigned
pulls:
# Number of days of inactivity before a Pull Request becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale Pull Request is closed
daysUntilClose: 14
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This pull request has been automatically marked as stale because it has not had recent
activity, and will be closed if no further activity occurs. If this pull request was
overlooked, forgotten, or should remain open for any other reason, please reply
here to call attention to it and remove the stale status. Thank you for your contributions.
# Comment to post when closing a stale Pull Request. Set to `false` to disable
closeComment: >
This pull request has been automatically closed because it has not had
recent activity. Please feel free to update or reopen it.
issues:
# Number of days of inactivity before an Issue becomes stale
daysUntilStale: 180
# Number of days of inactivity before a stale Issue is closed
daysUntilClose: 14
# Comment to post when marking as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had activity
in a long time. If this issue is still relevant and should remain open, please reply
with a short explanation (e.g. "I have checked the code and this issue is still relevant because ___."
or "Here's a screenshot of this issue on the latest version").
Thank you for your contributions!
# Comment to post when closing a stale Issue. Set to `false` to disable
closeComment: >
This issue has been automatically closed because it has not had
activity in a long time. Please feel free to reopen it or create a new issue.
-16
View File
@@ -1,16 +0,0 @@
name: 'Comment actions'
on: issue_comment
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/comment_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
-16
View File
@@ -1,16 +0,0 @@
name: 'Issue actions'
on: issues
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/issue_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
-13
View File
@@ -1,13 +0,0 @@
name: 'PR Review'
on: pull_request_review
jobs:
upload_payload:
name: Upload payload
runs-on: ubuntu-latest
steps:
- uses: actions/upload-artifact@v3 # Payload artifact is consumed in Actionable
with:
name: payload
path: ${{ github.event_path }}
@@ -1,13 +0,0 @@
name: 'PR Review Comment'
on: pull_request_review_comment
jobs:
upload_payload:
name: Upload payload
runs-on: ubuntu-latest
steps:
- uses: actions/upload-artifact@v3 # Payload artifact is consumed in Actionable
with:
name: payload
path: ${{ github.event_path }}
@@ -1,20 +0,0 @@
name: 'Pull Request Review Comment actions'
on:
workflow_run:
workflows: [PR Review Comment]
types:
- completed
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/pr_review_comment_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
@@ -1,20 +0,0 @@
name: 'Pull Request Review actions'
on:
workflow_run:
workflows: [PR Review]
types:
- completed
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/pr_review_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
-18
View File
@@ -1,18 +0,0 @@
name: 'Pull Request actions'
on:
pull_request_target:
types: [assigned, unassigned, labeled, unlabeled, opened, edited, closed, reopened, synchronize, converted_to_draft, ready_for_review, locked, unlocked, review_requested, review_request_removed, auto_merge_enabled, auto_merge_disabled]
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/pr_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
-16
View File
@@ -1,16 +0,0 @@
name: 'Push actions'
on: push
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
labels:
uses: MinecraftForge/SharedActions/.github/workflows/push_actions.yml@main
secrets:
GH_APP_KEY: ${{ secrets.GH_APP_KEY }}
GH_APP_NAME: ${{ secrets.GH_APP_NAME }}
+8 -48
View File
@@ -1,49 +1,9 @@
#eclipse
**/bin
**/.settings
**/.classpath
*.project
#In case people make their workspace in the repo
/eclipse/
#idea
/.idea
/*/out
/classes
*.iml
#gradle
/build
/.gradle
/*/build
/*/.gradle
# Projects repo, either ignore it, or ignore patches.
/projects/mcp/
/projects/clean/
/projects/forge/
/projects/fmlonly/
#/patches/*
#occupational hazards
/projects/**/build/
/projects/**/out/
/projects/**/run/
/projects/**/*.launch
/repo/
src/generated/resources/.cache/
src/generated_test/resources/.cache/
src/generated_mdk/resources/.cache/
#Patch rejects
#/patches-/
#*.patch.rej
#/projects/*/rejects/
# FML changelog
changelog.txt
*.py
*.7z
*.DS_Store
/logs/
/*.pyc
/forge-*/
/patches-old/
/mcp/
/eclipse/
/target/
/temp/
+3
View File
@@ -0,0 +1,3 @@
[submodule "fml"]
path = fml
url = https://github.com/MinecraftForge/FML.git
-104
View File
@@ -1,104 +0,0 @@
<?xml version="1.0"?>
<project>
<modelVersion>4.0.0</modelVersion>
<name>MinecraftForge_Forge Config DSL Script</name>
<groupId>MinecraftForge_Forge</groupId>
<artifactId>MinecraftForge_Forge_dsl</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.jetbrains.teamcity</groupId>
<artifactId>configs-dsl-kotlin-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<repositories>
<repository>
<id>jetbrains-all</id>
<url>https://download.jetbrains.com/teamcity-repository</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>teamcity-server</id>
<url>https://teamcity.minecraftforge.net/app/dsl-plugins-repository</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>JetBrains</id>
<url>https://download.jetbrains.com/teamcity-repository</url>
</pluginRepository>
</pluginRepositories>
<build>
<sourceDirectory>${basedir}</sourceDirectory>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${kotlin.version}</version>
<configuration/>
<executions>
<execution>
<id>compile</id>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>process-test-sources</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jetbrains.teamcity</groupId>
<artifactId>teamcity-configs-maven-plugin</artifactId>
<version>${teamcity.dsl.version}</version>
<configuration>
<format>kotlin</format>
<dstDir>target/generated-configs</dstDir>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.teamcity</groupId>
<artifactId>configs-dsl-kotlin</artifactId>
<version>${teamcity.dsl.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.teamcity</groupId>
<artifactId>configs-dsl-kotlin-plugins</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-script-runtime</artifactId>
<version>${kotlin.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
-208
View File
@@ -1,208 +0,0 @@
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.projectFeatures.githubIssues
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
/*
The settings script is an entry point for defining a TeamCity
project hierarchy. The script should contain a single call to the
project() function with a Project instance or an init function as
an argument.
VcsRoots, BuildTypes, Templates, and subprojects can be
registered inside the project using the vcsRoot(), buildType(),
template(), and subProject() methods respectively.
To debug settings scripts in command-line, run the
mvnDebug org.jetbrains.teamcity:teamcity-configs-maven-plugin:generate
command and attach your debugger to the port 8000.
To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View
-> Tool Windows -> Maven Projects), find the generate task node
(Plugins -> teamcity-configs -> teamcity-configs:generate), the
'Debug' option is available in the context menu for the task.
*/
version = "2021.2"
project {
buildType(Build)
buildType(BuildSecondaryBranches)
buildType(PullRequests)
buildType(PullRequestChecks)
buildType(PullRequestCompatibility)
params {
text("docker_jdk_version", "21", label = "Gradle version", description = "The version of the JDK to use during execution of tasks in a JDK.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
text("docker_gradle_version", "8.8", label = "Gradle version", description = "The version of Gradle to use during execution of Gradle tasks.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
text("git_main_branch", "1.20.x", label = "Git Main Branch", description = "The git main or default branch to use in VCS operations.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
text("git_branch_spec", """
+:refs/heads/(main*)
+:refs/heads/(master*)
+:refs/heads/(develop|release|staging|main|master)
+:refs/heads/(1.*)
""".trimIndent(), label = "The branch specification of the repository", description = "By default all main branches are build by the configuration. Modify this value to adapt the branches build.", display = ParameterDisplay.HIDDEN, allowEmpty = true)
text("github_repository_name", "MinecraftForge", label = "The github repository name. Used to connect to it in VCS Roots.", description = "This is the repository slug on github. So for example `MinecraftForge` or `MinecraftForge`. It is interpolated into the global VCS Roots.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
text("env.PUBLISHED_JAVA_ARTIFACT_ID", "forge", label = "Published artifact id", description = "The maven coordinate artifact id that has been published by this build. Can not be empty.", allowEmpty = false)
text("env.PUBLISHED_JAVA_FML_ARTIFACT_ID", "fmlonly", label = "Published fmlonly artifact id", description = "The maven coordinate artifact id for fml only that has been published by this build. Can not be empty.", allowEmpty = false)
text("env.PUBLISHED_JAVA_FML_ARTIFACT_VERSION", "0.0.0-SNAPSHOT", label = "Published fmlonly artifact version", description = "The version for fml only that has been published by this build. Can not be empty.", allowEmpty = false)
text("env.PUBLISHED_JAVA_GROUP", "net.minecraftforge", label = "Published group", description = "The maven coordinate group that has been published by this build. Can not be empty.", allowEmpty = false)
//These are references and not actually keys
password("env.CROWDIN_KEY", "credentialsJSON:a3102dbe-805d-4177-9f54-3d2c2eb08fd5", display = ParameterDisplay.HIDDEN)
password("env.KEYSTORE_URL", "credentialsJSON:a7ae1c82-8058-4061-8d12-7f6bc2618d2e", display = ParameterDisplay.HIDDEN)
password("env.KEYSTORE_PASSWORD", "credentialsJSON:d7b964e3-a1fd-47a8-b892-6f601fe47479", display = ParameterDisplay.HIDDEN)
text("additional_publishing_gradle_parameters", "-PcrowdinKey=%env.CROWDIN_KEY% -PkeystoreKeyPass=%env.KEYSTORE_PASSWORD% -PkeystoreStorePass=%env.KEYSTORE_PASSWORD% -Pkeystore=%env.KEYSTORE_URL%", label = "Additional gradle parameters for publish", description = "Contains the additional gradle parameters used during publishing.", display = ParameterDisplay.HIDDEN, allowEmpty = true)
checkbox("should_execute_build", "false", label = "Should build", description = "Indicates if the build task should be executed.", display = ParameterDisplay.HIDDEN,
checked = "true", unchecked = "false")
checkbox("should_execute_test", "false", label = "Should build", description = "Indicates if the build task should be executed.", display = ParameterDisplay.HIDDEN,
checked = "true", unchecked = "false")
}
features {
githubIssues {
id = "MinecraftForge_MinecraftForge__IssueTracker"
displayName = "MinecraftForge/MinecraftForge"
repositoryURL = "https://github.com/MinecraftForge/MinecraftForge"
}
}
}
object Build : BuildType({
templates(AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_BuildMainBranches"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"), AbsoluteId("MinecraftForge_BuildUsingGradle"), AbsoluteId("MinecraftForge_PublishProjectUsingGradle"), AbsoluteId("MinecraftForge_TriggersStaticFilesWebpageGenerator"))
id("MinecraftForge_MinecraftForge__Build")
name = "Build"
description = "Builds and Publishes the main branches of the project."
features {
feature {
id = "trigger_fml_only_files_generator"
type = "triggerBuildFeature"
param("triggers", "MinecraftForge_FilesGenerator_GeneratePages")
param("parameters", """
env.PUBLISHED_JAVA_FML_ARTIFACT_ID~env.PUBLISHED_JAVA_ARTIFACT_ID
env.PUBLISHED_JAVA_FML_ARTIFACT_VERSION~env.PUBLISHED_JAVA_ARTIFACT_VERSION
env.PUBLISHED_JAVA_GROUP
""".trimIndent())
}
}
})
object BuildSecondaryBranches : BuildType({
templates(AbsoluteId("MinecraftForge_ExcludesBuildingDefaultBranch"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_BuildMainBranches"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"), AbsoluteId("MinecraftForge_BuildUsingGradle"))
id("MinecraftForge_MinecraftForge__BuildSecondaryBranches")
name = "Build - Secondary Branches"
description = "Builds and Publishes the secondary branches of the project."
params {
text("git_branch_spec", """
+:refs/heads/(*)
-:refs/heads/noci*
""".trimIndent(), label = "The branch specification of the repository", description = "By default all main branches are build by the configuration. Modify this value to adapt the branches build.", display = ParameterDisplay.HIDDEN, allowEmpty = true)
}
vcs {
branchFilter = """
+:*
-:1.*
-:<default>
""".trimIndent()
}
})
object PullRequests : BuildType({
templates(AbsoluteId("MinecraftForge_BuildPullRequests"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"), AbsoluteId("MinecraftForge_BuildUsingGradle"))
id("MinecraftForge_MinecraftForge__PullRequests")
name = "Pull Requests"
description = "Builds pull requests for the project"
params {
checkbox("should_execute_build", "true", label = "Should build", description = "Indicates if the build task should be executed.", display = ParameterDisplay.HIDDEN,
checked = "true", unchecked = "false")
text(
"gradle_build_task",
"assemble",
label = "Gradle build task to execute during build",
description = "Determines the build task that is executed to build the project.",
display = ParameterDisplay.HIDDEN,
allowEmpty = false
)
}
vcs {
branchFilter = """
+:*
-:1.*
-:<default>
""".trimIndent()
}
})
object PullRequestChecks : BuildType({
templates(AbsoluteId("MinecraftForge_BuildPullRequests"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"))
id("MinecraftForge_MinecraftForge__PullRequestChecks")
name = "Pull Requests (Checks)"
description = "Checks pull requests for the project"
steps {
gradle {
name = "Check"
id = "RUNNER_10_Check"
tasks = "checkAll"
gradleParams = "--continue %gradle_custom_args%"
enableStacktrace = true
dockerImage = "%docker_gradle_image%"
dockerRunParameters = """
-v "/opt/cache/agent/gradle:/home/gradle/.gradle"
-v "/opt/cache/shared/gradle:/home/gradle/rocache:ro"
--network=host
-u 1000:1000
%docker_additional_args%
""".trimIndent()
}
}
vcs {
branchFilter = """
+:*
-:1.*
-:<default>
""".trimIndent()
}
})
object PullRequestCompatibility : BuildType({
templates(AbsoluteId("MinecraftForge_BuildPullRequests"), AbsoluteId("MinecraftForge_SetupGradleUtilsCiEnvironmen"), AbsoluteId("MinecraftForge_BuildWithDiscordNotifications"), AbsoluteId("MinecraftForge_SetupProjectUsingGradle"))
id("MinecraftForge_MinecraftForge__PullRequestCompatibility")
name = "Pull Requests (Compatibility)"
description = "Validates binary compatibility for pull requests made to the project"
steps {
gradle {
name = "Validate"
id = "RUNNER_10_Compatibility"
tasks = "checkJarCompatibility"
gradleParams = "--continue %gradle_custom_args%"
enableStacktrace = true
dockerImage = "%docker_gradle_image%"
dockerRunParameters = """
-v "/opt/cache/agent/gradle:/home/gradle/.gradle"
-v "/opt/cache/shared/gradle:/home/gradle/rocache:ro"
--network=host
-u 1000:1000
%docker_additional_args%
""".trimIndent()
}
}
vcs {
branchFilter = """
+:*
-:1.*
-:<default>
""".trimIndent()
}
})
-65
View File
@@ -1,65 +0,0 @@
Minecraft Forge: Credits/Thank You
Forge is a set of tools and modifications to the Minecraft base game code to assist
mod developers in creating new and exciting content. It has been in development for
several years now, but I would like to take this time thank a few people who have
helped it along it's way.
First, the people who originally created the Forge projects way back in Minecraft
alpha. Eloraam of RedPower, and SpaceToad of Buildcraft, without their acceptiance
of me taking over the project, who knows what Minecraft modding would be today.
Secondly, someone who has worked with me, and developed some of the core features
that allow modding to be as functional, and as simple as it is, cpw. For developing
FML, which stabelized the client and server modding ecosystem. As well as the base
loading system that allows us to modify Minecraft's code as elegently as possible.
Mezz, who has stepped up as the issue and pull request manager. Helping to keep me
sane as well as guiding the community into creating better additions to Forge.
Searge, Bspks, Fesh0r, ProfMobious, and all the rest over on the MCP team {of which
I am a part}. For creating some of the core tools needed to make Minecraft modding
both possible, and as stable as can be.
On that note, here is some specific information of the MCP data we use:
* Minecraft Coder Pack (MCP) *
Forge Mod Loader and Minecraft Forge have permission to distribute and automatically
download components of MCP and distribute MCP data files. This permission is not
transitive and others wishing to redistribute the Minecraft Forge source independently
should seek permission of MCP or remove the MCP data files and request their users
to download MCP separately.
And lastly, the countless community members who have spent time submitting bug reports,
pull requests, and just helping out the community in general. Thank you.
--LexManos
=========================================================================
This is Forge Mod Loader.
You can find the source code at all times at https://github.com/MinecraftForge/MinecraftForge/tree/1.12.x/src/main/java/net/minecraftforge/fml
This minecraft mod is a clean open source implementation of a mod loader for minecraft servers
and minecraft clients.
The code is authored by cpw.
It began by partially implementing an API defined by the client side ModLoader, authored by Risugami.
http://www.minecraftforum.net/topic/75440-
This support has been dropped as of Minecraft release 1.7, as Risugami no longer maintains ModLoader.
It also contains suggestions and hints and generous helpings of code from LexManos, author of MinecraftForge.
http://www.minecraftforge.net/
Additionally, it contains an implementation of topological sort based on that
published at http://keithschwarz.com/interesting/code/?dir=topological-sort
It also contains code from the Maven project for performing versioned dependency
resolution. http://maven.apache.org/
It also contains a partial repackaging of the javaxdelta library from http://sourceforge.net/projects/javaxdelta/
with credit to it's authors.
Forge Mod Loader downloads components from the Minecraft Coder Pack
(http://mcp.ocean-labs.de/index.php/Main_Page) with kind permission from the MCP team.
-2
View File
@@ -1,2 +0,0 @@
Copyright (c) Forge Development LLC and contributors
SPDX-License-Identifier: LGPL-2.1-only
-523
View File
@@ -1,523 +0,0 @@
Unless noted below, Minecraft Forge, Forge Mod Loader, and all
parts herein are licensed under the terms of the LGPL 2.1 found
here http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt and
copied below.
Homepage: http://minecraftforge.net/
https://github.com/MinecraftForge/MinecraftForge
A note on authorship:
All source artifacts are property of their original author, with
the exclusion of the contents of the patches directory and others
copied from it from time to time. Authorship of the contents of
the patches directory is retained by the Minecraft Forge project.
This is because the patches are partially machine generated
artifacts, and are changed heavily due to the way forge works.
Individual attribution within them is impossible.
Consent:
All contributions to Forge must consent to the release of any
patch content to the Forge project.
A note on infectivity:
The LGPL is chosen specifically so that projects may depend on Forge
features without being infected with its license. That is the
purpose of the LGPL. Mods and others using this code via ordinary
Java mechanics for referencing libraries are specifically not bound
by Forge's license for the Mod code.
=== MCP Data ===
This software includes data from the Minecraft Coder Pack (MCP), with kind permission
from them. The license to MCP data is not transitive - distribution of this data by
third parties requires independent licensing from the MCP team. This data is not
redistributable without permission from the MCP team.
=== Sharing ===
I grant permission for some parts of FML to be redistributed outside the terms of the LGPL, for the benefit of
the minecraft modding community. All contributions to these parts should be licensed under the same additional grant.
-- Runtime patcher --
License is granted to redistribute the runtime patcher code (src/main/java/net/minecraftforge/fml/common/patcher
and subdirectories) under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
-- ASM transformers --
License is granted to redistribute the ASM transformer code (src/main/java/net/minecraftforge/common/asm/ and subdirectories)
under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
=========================================================================
This software includes portions from the Apache Maven project at
http://maven.apache.org/ specifically the ComparableVersion.java code. It is
included based on guidelines at
http://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html
with notices intact. The only change is a non-functional change of package name.
This software contains a partial repackaging of javaxdelta, a BSD licensed program for generating
binary differences and applying them, sourced from the subversion at http://sourceforge.net/projects/javaxdelta/
authored by genman, heikok, pivot.
The only changes are to replace some Trove collection types with standard Java collections, and repackaged.
This software includes the Monocraft font from https://github.com/IdreesInc/Monocraft/ for use in the early loading
display.
=========================================================================
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
+95
View File
@@ -0,0 +1,95 @@
import os
import sys
import fnmatch
import shlex
import difflib
import time, subprocess
from optparse import OptionParser
def cmdsplit(args):
if os.sep == '\\':
args = args.replace('\\', '\\\\')
return shlex.split(args)
def main():
print("Applying patches")
parser = OptionParser()
parser.add_option('-m', '--mcp-dir', action='store', dest='mcp_dir', help='Path to MCP', default=None)
parser.add_option('-p', '--patch-dir', action='store', dest='patch_dir', help='Patches Directory', default=None)
parser.add_option('-t', '--target-dir', action='store', dest='target_dir', help='Target Directory', default=None)
options, _ = parser.parse_args()
if options.mcp_dir is None:
print 'Must supply MCP directory with --mcp-dir'
return
elif options.patch_dir is None:
print 'Must supply patches directory with --patch-dir'
return
elif options.target_dir is None:
print 'Must supplt target directory with --target-dir'
return
print "Applying patches from '%s' to '%s'" % (options.patch_dir, options.target_dir)
apply_patches(options.mcp_dir, options.patch_dir, options.target_dir)
def apply_patches(mcp_dir, patch_dir, target_dir, find=None, rep=None):
# Attempts to apply a directory full of patch files onto a target directory.
sys.path.append(mcp_dir)
temp = os.path.abspath('temp.patch')
cmd = cmdsplit('patch -p2 -i "%s" ' % temp)
if os.name == 'nt':
applydiff = os.path.abspath(os.path.join(mcp_dir, 'runtime', 'bin', 'applydiff.exe'))
cmd = cmdsplit('"%s" -uf -p2 -i "%s"' % (applydiff, temp))
for path, _, filelist in os.walk(patch_dir, followlinks=True):
for cur_file in fnmatch.filter(filelist, '*.patch'):
patch_file = os.path.normpath(os.path.join(patch_dir, path[len(patch_dir)+1:], cur_file))
target_file = os.path.join(target_dir, fix_patch(patch_file, temp, find, rep))
process = subprocess.Popen(cmd, cwd=target_dir, bufsize=-1)
process.communicate()
if os.path.isfile(temp):
os.remove(temp)
def fix_patch(in_file, out_file, find=None, rep=None):
#Fixes the following issues in the patch file if they exist:
# Normalizes the path seperators for the current OS
# Normalizes the line endings
# Returns the path that the file wants to apply to
in_file = os.path.normpath(in_file)
if out_file is None:
tmp_file = in_file + '.tmp'
else:
out_file = os.path.normpath(out_file)
tmp_file = out_file
dir_name = os.path.dirname(out_file)
if dir_name:
if not os.path.exists(dir_name):
os.makedirs(dir_name)
file = 'not found'
with open(in_file, 'rb') as inpatch:
with open(tmp_file, 'wb') as outpatch:
for line in inpatch:
line = line.rstrip('\r\n')
if line[:3] in ['+++', '---', 'Onl', 'dif']:
if not find == None and not rep == None:
line = line.replace('\\', '/').replace(find, rep).replace('/', os.sep)
else:
line = line.replace('\\', '/').replace('/', os.sep)
outpatch.write(line + os.linesep)
else:
outpatch.write(line + os.linesep)
if line[:3] == '---':
file = line[line.find(os.sep, line.find(os.sep)+1)+1:]
if out_file is None:
shutil.move(tmp_file, in_file)
return file
if __name__ == '__main__':
main()
-1822
View File
File diff suppressed because it is too large. Load diff
+34
View File
@@ -0,0 +1,34 @@
import os, os.path, sys
from optparse import OptionParser
forge_dir = os.path.dirname(os.path.abspath(__file__))
mcp_dir = os.path.abspath('..')
from forge import build_forge_dev
def main():
parser = OptionParser()
parser.add_option('-m', '--mcp-dir', action='store', dest='mcp_dir', help='MCP Path', default=None)
parser.add_option('-b', '--build', action='store', dest='build', help='Build number', default=None)
options, _ = parser.parse_args()
build_num = 0
if not options.build is None:
try:
build_num = int(options.build)
except:
pass
fml_dir = os.path.join(forge_dir, 'fml')
mcp_dir = os.path.join(forge_dir, 'mcp')
if not options.mcp_dir is None:
mcp_dir = os.path.abspath(options.mcp_dir)
elif os.path.isfile(os.path.join('..', 'runtime', 'commands.py')):
mcp_dir = os.path.abspath('..')
sys.exit(build_forge_dev(mcp_dir, forge_dir, fml_dir, build_num))
if __name__ == '__main__':
main()
-3
View File
@@ -1,3 +0,0 @@
/.gradle/
/build/
/out/
-13
View File
@@ -1,13 +0,0 @@
repositories {
maven { url = 'https://maven.minecraftforge.net/' }
mavenCentral()
}
dependencies {
implementation 'org.ow2.asm:asm:9.5'
implementation 'org.ow2.asm:asm-tree:9.5'
implementation 'net.minecraftforge:srgutils:0.5.+'
implementation 'commons-io:commons-io:2.12.0'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r'
}
@@ -1,50 +0,0 @@
package net.minecraftforge.forge.tasks
import groovy.json.JsonBuilder
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.TaskAction
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.FieldNode
import org.objectweb.asm.tree.MethodNode
abstract class BytecodeFinder extends DefaultTask {
@InputFile abstract RegularFileProperty getJar()
// It should be fine to mark the output as internal as we want to control when we run it anyways.
// This also shuts Gradle 8 up about implicit task dependencies.
@Internal abstract RegularFileProperty getOutput()
BytecodeFinder() {
output.convention(project.layout.buildDirectory.dir(name).map { it.file("output.json") })
}
@TaskAction
protected void exec() {
Util.init()
def outputFile = output.get().asFile
if (outputFile.exists())
outputFile.delete()
pre()
Util.processClassNodes(jar.get().asFile, this::process)
post()
outputFile.text = new JsonBuilder(getData()).toPrettyString()
}
protected process(ClassNode node) {
if (node.fields != null) node.fields.each { process(node, it) }
if (node.methods != null) node.methods.each { process(node, it) }
}
protected pre() {}
protected process(ClassNode parent, FieldNode node) {}
protected process(ClassNode parent, MethodNode node) {}
protected post() {}
protected abstract Object getData()
}
@@ -1,32 +0,0 @@
package net.minecraftforge.forge.tasks
import groovy.transform.CompileStatic
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Internal
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.MethodNode
@CompileStatic
abstract class BytecodePredicateFinder extends BytecodeFinder {
@Internal
abstract Property<Closure<Boolean>> getPredicate()
private final Set<String> matches = new HashSet()
@Override
protected process(ClassNode parent, MethodNode node) {
for (final current : node.instructions) {
if (predicate.get().call(parent, node, current)) {
matches.add(parent.name)
return
}
}
}
@Internal
@Override
protected Object getData() {
return matches.stream().sorted().toList() ?: {throw new RuntimeException('Failed to find any targets, please ensure that method names and descriptors are correct.')}()
}
}
@@ -1,31 +0,0 @@
package net.minecraftforge.forge.tasks
import java.util.function.BiConsumer
public class ClosureHelper {
BiConsumer<String, Closure> callback
public ClosureHelper(Closure cl, BiConsumer<String, Closure> callback) {
this.callback = callback
apply(this, cl)
}
def methodMissing(String name, Object args) {
if (!args.class.isArray()) return
Object[] aargs = (Object[])args
if (aargs.length == 1 && aargs[0] instanceof Closure) {
this.callback.accept(name, (Closure)aargs[0])
} else {
throw new IllegalArgumentException('Unknown method: "' + name + '" with arguments ' + args + ' for ' + this)
}
}
static <T> T apply(T obj, Closure cl) {
cl.delegate = obj
cl.resolveStrategy = Closure.DELEGATE_FIRST
cl()
return obj
}
}
@@ -1,73 +0,0 @@
package net.minecraftforge.forge.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
abstract class Crowdin extends DefaultTask {
@Input abstract Property<String> getId()
@Input @Optional abstract Property<String> getKey()
@Input boolean json = true
@OutputFile abstract RegularFileProperty getOutput()
@OutputFile abstract RegularFileProperty getExport()
Crowdin() {
outputs.upToDateWhen{ false }
id.convention('minecraft-forge')
output.convention(project.layout.buildDirectory.dir(name).map {it.file("output.zip") })
export.convention(project.layout.buildDirectory.dir(name).map {it.file("export.json") })
}
@TaskAction
def run() {
File outputFile = output.get().asFile
File exportFile = export.get().asFile
if (outputFile.exists())
outputFile.delete()
if (!key.isPresent())
return
String key = this.key.get()
String id = this.id.get()
// Force an export
new URL("https://api.crowdin.com/api/project/${id}/export?key=${key}").withInputStream { i ->
exportFile.withOutputStream { it << i }
}
if (!exportFile.text.contains('success')) {
throw new RuntimeException("Crowdin export failed, see ${exportFile} for more info")
}
new URL("https://api.crowdin.com/api/project/${id}/download/all.zip?key=${key}").withInputStream { i ->
new ZipInputStream(i).withCloseable { zin ->
outputFile.withOutputStream { out ->
new ZipOutputStream(out).withCloseable { zout ->
ZipEntry zein
while ((zein = zin.nextEntry) != null) {
if (zein.isDirectory()) {
zout.putNextEntry(new ZipEntry(zein.name))
} else {
// 1.13+ uses json
if (zein.name.endsWith('.json') == json) {
ZipEntry zeout = new ZipEntry(json ? zein.name.toLowerCase() : zein.name)
zeout.time = 1
zout.putNextEntry(zeout)
zout << zin
}
}
}
}
}
}
}
}
}
@@ -1,54 +0,0 @@
package net.minecraftforge.forge.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.OutputFiles
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.OutputDirectory
import java.nio.file.Files
abstract class DownloadLibraries extends DefaultTask {
@InputFile abstract RegularFileProperty getInput()
@OutputDirectory abstract DirectoryProperty getOutput()
@OutputFile abstract RegularFileProperty getLibrariesOutput()
DownloadLibraries() {
output.convention(project.layout.buildDirectory.dir(name))
librariesOutput.convention(project.layout.buildDirectory.dir(name).map(d -> d.file('libraries.txt')))
}
@TaskAction
def run() {
Util.init()
File outputDir = output.get().asFile
def libraries = new ArrayList()
def json = input.get().asFile.json().libraries.each { lib ->
//TODO: Thread?
def artifacts = [lib.downloads.artifact] + lib.downloads.get('classifiers', [:]).values()
artifacts.each{ art ->
def target = new File(outputDir, art.path)
libraries.add(target.absolutePath)
if (!target.exists() || !art.sha1.equals(target.sha1())) {
project.logger.lifecycle("Downloading ${art.url}")
if (!target.parentFile.exists()) {
target.parentFile.mkdirs()
}
new URL(art.url).withInputStream { i ->
target.withOutputStream { it << i }
}
if (!art.sha1.equals(target.sha1())) {
throw new IllegalStateException("Failed to download ${art.url} to ${target.canonicalPath} SHA Mismatch")
}
}
}
}
Files.write(librariesOutput.get().asFile.toPath(), libraries)
}
}
@@ -1,109 +0,0 @@
package net.minecraftforge.forge.tasks
import groovy.transform.EqualsAndHashCode
import java.util.ArrayList
import java.util.HashMap
import java.util.TreeMap
import java.util.TreeSet
import java.util.function.BiConsumer
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.*
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.FieldNode
import org.objectweb.asm.tree.MethodNode
import static org.objectweb.asm.Opcodes.*
abstract class FieldCompareFinder extends BytecodeFinder {
@Nested
Map<String, Search> fields = [:] as HashMap
@Internal
Map<Search, String> fieldsReverse = [:] as HashMap
@Internal
Map<String, Set<ObjectTarget>> targets = [:] as TreeMap
@Override
protected pre() {
//fields.each{ k,v -> logger.lifecycle("Fields: " + k + ' ' + v) }
}
@Override
protected process(ClassNode parent, MethodNode node) {
def last = null
def parentInstance = new ObjectTarget(owner: parent.name, name: '', desc: '')
for (int x = 0; x < node.instructions.size(); x++) {
def current = node.instructions.get(x)
if (current.opcode == IF_ACMPEQ || current.opcode == IF_ACMPNE) {
if (last != null && (last.opcode == GETSTATIC || last.opcode == GETFIELD)) {
def target = new Search(cls: last.owner, name: last.name)
def wanted = fieldsReverse.get(target)
def original = fields.get(wanted)
def instance = new ObjectTarget(owner: parent.name, name: node.name, desc: node.desc)
if (wanted != null && (original.blacklist == null || (!original.blacklist.contains(instance) && !original.blacklist.contains(parentInstance)))) {
targets.computeIfAbsent(wanted, { k -> new TreeSet() }).add(instance)
}
}
}
last = current
}
}
@Internal
@Override
protected Object getData() {
def ret = [:] as HashMap
targets.forEach{ k, v ->
def e = fields.get(k)
ret[k] = [
cls: e.cls,
name: e.name,
replacement: e.replacement,
targets: v
]
}
return ret
}
@EqualsAndHashCode(excludes = ['replacement', 'blacklist'])
public static class Search {
@Input
String cls
@Input
String name
@Input
String replacement
@Nested
@Optional
Set<ObjectTarget> blacklist
@Override
String toString() {
return cls + '.' + name
}
def blacklist(def owner, def name, def desc) {
if (blacklist == null)
blacklist = new HashSet()
blacklist.add(new ObjectTarget(owner: owner, name: name, desc: desc))
}
def blacklist(def owner) {
blacklist(owner, '', '')
}
}
def fields(Closure cl) {
new ClosureHelper(cl, {name, ccl ->
def search = ClosureHelper.apply(new Search(), ccl)
this.fields.put(name, search)
this.fieldsReverse.put(search, name)
})
}
}
@@ -1,40 +0,0 @@
package net.minecraftforge.forge.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.TaskAction
import java.nio.file.Files
abstract class FixPatchImports extends DefaultTask {
@InputDirectory abstract DirectoryProperty getClean()
@InputDirectory abstract DirectoryProperty getPatched()
@TaskAction
void run() {
final patchedPath = getPatched().get().asFile.toPath()
final cleanPath = getClean().get().asFile.toPath()
try (final var stream = Files.find(patchedPath, Integer.MAX_VALUE, (path, attr) -> path.toString().endsWith('.java'))) {
final itr = stream.iterator()
while (itr.hasNext()) {
final next = itr.next()
final clean = cleanPath.resolve(patchedPath.relativize(next).toString())
final patchedLines = Files.readAllLines(next)
final cleanLines = Files.readAllLines(clean)
final patchedImport = patchedLines.findLastIndexOf(2) { it.startsWith('import ') }
final cleanImport = cleanLines.findLastIndexOf(2) { it.startsWith('import ') }
if (cleanImport !== patchedImport) {
patchedLines.removeIf { it.startsWith('import ') }
patchedLines.addAll(2, cleanLines.subList(2, cleanImport + 1))
Files.write(next, patchedLines)
}
}
}
}
}
@@ -1,49 +0,0 @@
package net.minecraftforge.forge.tasks
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import groovy.transform.CompileStatic
import groovy.transform.TupleConstructor
@CompileStatic
@TupleConstructor
final class InheritanceData implements Annotatable {
String name
int access
String superName
List<String> interfaces = []
Map<String, Method> methods = [:]
Map<String, Field> fields = [:]
List<Annotation> annotations = []
@TupleConstructor
static final class Method implements Annotatable {
int access
String override
List<Annotation> annotations = []
}
@TupleConstructor
static final class Field implements Annotatable {
int access
String desc
List<Annotation> annotations = []
}
@TupleConstructor
static final class Annotation {
String desc
}
private static final Gson GSON = new Gson()
static Map<String, InheritanceData> parse(File file) {
try (final reader = file.newReader()) {
return GSON.fromJson(reader, new TypeToken<Map<String, InheritanceData>>() {})
}
}
}
@CompileStatic
interface Annotatable {
List<InheritanceData.Annotation> getAnnotations()
}
@@ -1,44 +0,0 @@
package net.minecraftforge.forge.tasks
import org.gradle.api.tasks.bundling.Zip
import org.gradle.api.tasks.*
import org.gradle.api.provider.SetProperty
abstract class InstallerJar extends Zip {
@Input @Optional abstract SetProperty<String> getPackedDependencies()
InstallerJar() {
archiveClassifier.set('installer')
archiveExtension.set('jar') // Needs to be Zip task to not override Manifest, so set extension
destinationDirectory.set(project.layout.buildDirectory.dir('libs'))
def installerJson = project.tasks.installerJson
def launcherJson = project.tasks.launcherJson
def downloadInstaller = project.tasks.downloadInstaller
dependsOn(installerJson, launcherJson, downloadInstaller)
from(installerJson, launcherJson)
from(project.rootProject.file('/src/main/resources/url.png'))
project.afterEvaluate {
from(project.zipTree(downloadInstaller.output)) {
duplicatesStrategy = 'exclude'
}
if (!System.env.TEAMCITY_VERSION) {
dependsOn(project.universalJar)
from(project.universalJar) {
into '/maven/' + project.group.replace('.', '/') + '/' + project.universalJar.archiveBaseName.get() + '/' + project.version + '/'
}
packedDependencies.get().forEach {
def jarTask = project.rootProject.getTasks().findByPath(it)
dependsOn(jarTask)
from(jarTask) {
into '/maven/' + jarTask.project.group.replace('.', '/') + '/' + jarTask.archiveBaseName.get() + '/' + jarTask.project.version + '/'
}
}
}
}
}
}
@@ -1,81 +0,0 @@
package net.minecraftforge.forge.tasks
import groovy.json.JsonBuilder
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.*
import java.nio.file.Files
abstract class InstallerJson extends DefaultTask {
@OutputFile abstract RegularFileProperty getOutput()
@InputFiles abstract ConfigurableFileCollection getInput()
@Input @Optional abstract SetProperty<String> getPackedDependencies()
@Input @Optional final Map<String, Object> libraries = new LinkedHashMap<>()
@Input Map<String, Object> json = new LinkedHashMap<>()
@InputFile abstract RegularFileProperty getIcon()
@Input abstract Property<String> getLauncherJsonName()
@Input abstract Property<String> getLogo()
@Input abstract Property<String> getMirrors()
@Input abstract Property<String> getWelcome()
InstallerJson() {
getLauncherJsonName().convention('/version.json')
getLogo().convention('/big_logo.png')
getMirrors().convention('https://files.minecraftforge.net/mirrors-2.0.json')
getWelcome().convention("Welcome to the simple ${project.name.capitalize()} installer.")
getOutput().convention(project.layout.buildDirectory.file('install_profile.json'))
['client', 'server'].each { side ->
['slim', 'extra'].each { type ->
def tsk = project.tasks.getByName("download${side.capitalize()}${type.capitalize()}")
dependsOn(tsk)
input.from(tsk.output)
}
def tsk = project.tasks.getByName("create${side.capitalize()}SRG")
dependsOn(tsk)
input.from(tsk.output)
}
project.afterEvaluate {
(packedDependencies.get().collect{ project.rootProject.tasks.findByPath(it) } + [project.universalJar]).forEach {
dependsOn(it)
input.from it.archiveFile
}
}
}
@TaskAction
protected void exec() {
def libs = libraries
for (def child : packedDependencies.get().collect{ project.rootProject.tasks.findByPath(it) } + [project.universalJar]) {
def dep = Util.getMavenDep(child)
def path = Util.getMavenPath(child)
libs.put(dep.toString(), [
name: dep,
downloads: [
artifact: [
path: path,
url: "https://maven.minecraftforge.net/${path}",
sha1: child.archiveFile.get().asFile.sha1(),
size: child.archiveFile.get().asFile.length()
]
]
])
}
json.libraries = libs.values().sort{a,b -> a.name.compareTo(b.name)}
json.icon = "data:image/png;base64," + new String(Base64.getEncoder().encode(Files.readAllBytes(icon.get().asFile.toPath())))
json.json = launcherJsonName.get()
json.logo = logo.get()
if (!mirrors.get().isEmpty())
json.mirrorList = mirrors.get()
json.welcome = welcome.get()
Files.writeString(output.get().getAsFile().toPath(), new JsonBuilder(json).toPrettyString())
}
}
@@ -1,74 +0,0 @@
package net.minecraftforge.forge.tasks
import groovy.json.JsonBuilder
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.*
import java.nio.file.Files
import static net.minecraftforge.forge.tasks.Util.getArtifacts
import static net.minecraftforge.forge.tasks.Util.iso8601Now
abstract class LauncherJson extends DefaultTask {
@OutputFile abstract RegularFileProperty getOutput()
@InputFiles abstract ConfigurableFileCollection getInput()
@Input Map<String, Object> json = new LinkedHashMap<>()
@Input @Optional abstract SetProperty<String> getPackedDependencies()
@Internal final vanilla = project.project(':mcp').file('build/mcp/downloadJson/version.json')
@Internal final timestamp = iso8601Now()
@Internal final comment = [
"Please do not automate the download and installation of Forge.",
"Our efforts are supported by ads from the download page.",
"If you MUST automate this, please consider supporting the project through https://www.patreon.com/LexManos/"
]
@Internal final id = "${project.rootProject.ext.MC_VERSION}-${project.name}${project.version.substring(project.rootProject.ext.MC_VERSION.length())}"
LauncherJson() {
getOutput().convention(project.layout.buildDirectory.file('version.json'))
dependsOn(':fmlloader:jar', 'universalJar')
getInput().from(project.tasks.universalJar.archiveFile,
project.project(':fmlloader').jar.archiveFile,
vanilla)
project.afterEvaluate {
packedDependencies.get().forEach {
def jarTask = project.rootProject.tasks.findByPath(it)
dependsOn(jarTask)
input.from jarTask.archiveFile
}
}
}
@TaskAction
protected void exec() {
if (!json.libraries)
json.libraries = []
def libs = [:]
getArtifacts(project, project.configurations.installer, false).each { key, lib -> libs[key] = lib }
getArtifacts(project, project.configurations.moduleonly, false).each { key, lib -> libs[key] = lib }
packedDependencies.get().collect{ project.rootProject.tasks.findByPath(it) }.forEach {
def path = Util.getMavenPath(it)
def key = Util.getMavenDep(it)
libs[key] = [
name: key,
downloads: [
artifact: [
path: path,
url: "https://maven.minecraftforge.net/${path}",
sha1: it.archiveFile.get().asFile.sha1(),
size: it.archiveFile.get().asFile.length()
]
]
]
}
libs.each { key, lib -> json.libraries.add(lib) }
Files.writeString(output.get().asFile.toPath(), new JsonBuilder(json).toPrettyString())
}
}
@@ -1,44 +0,0 @@
package net.minecraftforge.forge.tasks
import org.apache.commons.io.IOUtils
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
abstract class MergeJars extends DefaultTask {
MergeJars() {
output.convention(project.layout.buildDirectory.dir(name).map { it.file('output.jar') })
}
@TaskAction
void run() {
def jars = inputJars.files
try (ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(output.get().asFile))) {
for (def jar : jars) {
try (ZipInputStream zin = new ZipInputStream(new FileInputStream(jar))) {
def entry
while ((entry = zin.getNextEntry()) != null) {
ZipEntry _new = new ZipEntry(entry.getName())
_new.setTime(0) //SHOULD be the same time as the main entry, but NOOOO _new.setTime(entry.getTime()) throws DateTimeException, so you get 0, screw you!
zout.putNextEntry(_new)
IOUtils.copy(zin, zout)
}
}
}
}
}
@InputFiles
abstract ConfigurableFileCollection getInputJars()
@OutputFile
abstract RegularFileProperty getOutput()
}
@@ -1,32 +0,0 @@
package net.minecraftforge.forge.tasks
import groovy.transform.EqualsAndHashCode
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Nested
import org.gradle.api.tasks.Optional
@EqualsAndHashCode
public class ObjectTarget implements Comparable<ObjectTarget> {
@Input
String owner
@Input
String name
@Input
@Optional
String desc
@Override
String toString() {
if (desc == null)
return owner + '.' + name
return owner + '.' + name + desc
}
@Override
int compareTo(ObjectTarget o) {
return toString().compareTo(o.toString())
}
}
@@ -1,57 +0,0 @@
package net.minecraftforge.forge.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.Dependency
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import java.nio.file.Files
import java.nio.file.StandardCopyOption
abstract class SetupCheckJarCompatibility extends DefaultTask {
SetupCheckJarCompatibility() {
group = 'jar compatibility'
onlyIf {
inputVersion.getOrNull() != null
}
outputs.upToDateWhen { false } // Never up to date, because this setup task should always run
baseBinPatchesOutput.convention(project.layout.buildDirectory.dir(name).map { it.file('joined.lzma') })
}
@TaskAction
void run() {
def inputVersion = inputVersion.get()
def fmlLibs = project.configurations.detachedConfiguration(project.PACKED_DEPS.collect {
def artifactId = it.split(':')[1]
return project.dependencies.create("net.minecraftforge:${artifactId}:${inputVersion}")
}.toArray(Dependency[]::new))
project.tasks.named('checkJarCompatibility') {
baseLibraries.from(project.provider {
fmlLibs.resolvedConfiguration.lenientConfiguration.files
})
}
def baseForgeUserdev = project.layout.buildDirectory.dir(name).map { it.file("forge-${inputVersion}-userdev.jar") }.get().asFile
project.rootProject.extensions.download.run {
src "https://maven.minecraftforge.net/net/minecraftforge/forge/${inputVersion}/forge-${inputVersion}-userdev.jar"
dest baseForgeUserdev
}
def joinedLzma = project.zipTree(baseForgeUserdev).matching { it.include('joined.lzma') }.singleFile
Files.copy(joinedLzma.toPath(), baseBinPatchesOutput.get().asFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
@Input
@Optional
abstract Property<String> getInputVersion()
@OutputFile
abstract RegularFileProperty getBaseBinPatchesOutput()
}
@@ -1,74 +0,0 @@
package net.minecraftforge.forge.tasks
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import groovy.transform.CompileStatic
import org.eclipse.jgit.api.Git
import javax.annotation.Nullable
import java.util.stream.Collectors
@CompileStatic
class TeamcityRequests {
public static final Gson GSON = new Gson()
@Nullable
static <T> T jsonRequest(TypeToken<T> clazz, String url) throws Exception {
final HttpURLConnection conn = (HttpURLConnection) URI.create(url).toURL().openConnection()
conn.setRequestProperty('Accept', 'application/json')
conn.setReadTimeout(5 * 1000)
conn.setConnectTimeout(5 * 1000)
conn.connect()
if (conn.responseCode !== 200) {
return null
}
try (final InputStream is = conn.getInputStream()) {
GSON.fromJson(new InputStreamReader(is), clazz.getType())
}
}
@Nullable
static Map<String, Build> buildsByCommit() throws IOException {
final Map<String, Build> builds = [:]
jsonRequest(new TypeToken<Builds>() {}, 'https://teamcity.minecraftforge.net/guestAuth/app/rest/builds?fields=build:(revisions,number)&locator=buildType:(id:MinecraftForge_MinecraftForge_MinecraftForge_MinecraftForge__Build),defaultFilter:false,count:300,status:SUCCESS')
?.build?.forEach {
it.revisions.revision.each { rev -> builds[rev.version] = it }
}
return builds
}
@Nullable
static String attemptFindBase(File gitPath) {
try (final git = Git.open(gitPath)) {
final Map<String, Build> buildByCommit = buildsByCommit()
for (final commit in git.log().setMaxCount(100).call()) {
// Find the first commit which was built on CI
final build = buildByCommit[commit.id.name]
if (build) {
return build.number
}
}
} catch (Exception ignored) {}
return null
}
static final class Builds {
public List<Build> build
}
static final class Build {
public String number
public Revisions revisions
}
static final class Revisions {
public int count
public List<Revision> revision
}
static final class Revision {
public String version
}
}
@@ -1,165 +0,0 @@
package net.minecraftforge.forge.tasks
import groovy.json.JsonBuilder
import groovy.json.JsonSlurper
import org.objectweb.asm.ClassReader
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.ClassNode
import java.io.File
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
public class Util {
static final ASM_LEVEL = Opcodes.ASM9
static void init() {
File.metaClass.sha1 = { ->
MessageDigest md = MessageDigest.getInstance('SHA-1')
delegate.eachByte 4096, {bytes, size ->
md.update(bytes, 0, size)
}
return md.digest().collect {String.format "%02x", it}.join()
}
File.metaClass.getSha1 = { !delegate.exists() ? null : delegate.sha1() }
File.metaClass.json = { -> new JsonSlurper().parseText(delegate.text) }
File.metaClass.getJson = { return delegate.exists() ? new JsonSlurper().parse(delegate) : [:] }
File.metaClass.setJson = { json -> delegate.text = new JsonBuilder(json).toPrettyString() }
Date.metaClass.iso8601 = { ->
def format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
def result = format.format(delegate)
return result[0..21] + ':' + result[22..-1]
}
String.metaClass.rsplit = { String del, int limit = -1 ->
def lst = new ArrayList()
def x = 0, idx
def tmp = delegate
while ((idx = tmp.lastIndexOf(del)) != -1 && (limit == -1 || x++ < limit)) {
lst.add(0, tmp.substring(idx + del.length(), tmp.length()))
tmp = tmp.substring(0, idx)
}
lst.add(0, tmp)
return lst
}
}
public static String[] getClasspath(project, libs, artifact) {
def ret = []
artifactTree(project, artifact).each { key, lib ->
libs[lib.name] = lib
if (lib.name != artifact)
ret.add(lib.name)
}
return ret
}
public static def getArtifacts(project, config, classifiers) {
def ret = [:]
config.resolvedConfiguration.resolvedArtifacts.each {
def art = [
group: it.moduleVersion.id.group,
name: it.moduleVersion.id.name,
version: it.moduleVersion.id.version,
classifier: it.classifier,
extension: it.extension,
file: it.file
]
def key = art.group + ':' + art.name
def folder = "${art.group.replace('.', '/')}/${art.name}/${art.version}/"
def filename = "${art.name}-${art.version}"
if (art.classifier != null)
filename += "-${art.classifier}"
filename += ".${art.extension}"
def path = "${folder}${filename}"
def url = "https://libraries.minecraft.net/${path}"
if (!checkExists(url)) {
url = "https://maven.minecraftforge.net/${path}"
}
ret[key] = [
name: "${art.group}:${art.name}:${art.version}" + (art.classifier == null ? '' : ":${art.classifier}") + (art.extension == 'jar' ? '' : "@${art.extension}"),
downloads: [
artifact: [
path: path,
url: url,
sha1: sha1(art.file),
size: art.file.length()
]
]
]
}
return ret
}
public static def getMavenPath(task) {
def classifier = task.archiveClassifier.get()
def dep = "${task.project.group}:${task.project.name}:${task.project.version}" + (classifier == '' ? '' : ':' + classifier)
return "${task.project.group.replace('.', '/')}/${task.project.name}/${task.project.version}/${task.project.name}-${task.project.version}".toString() + (classifier == '' ? '' : '-' + classifier) + '.jar'
}
public static def getMavenDep(task) {
def classifier = task.archiveClassifier.get()
return "${task.project.group}:${task.project.name}:${task.project.version}" + (classifier == '' ? '' : ':' + classifier)
}
public static def iso8601Now() { new Date().iso8601() }
public static def sha1(file) {
MessageDigest md = MessageDigest.getInstance('SHA-1')
file.eachByte 4096, {bytes, size ->
md.update(bytes, 0, size)
}
return md.digest().collect {String.format "%02x", it}.join()
}
private static def artifactTree(project, artifact) {
if (!project.ext.has('tree_resolver'))
project.ext.tree_resolver = 1
def cfg = project.configurations.create('tree_resolver_' + project.ext.tree_resolver++)
def dep = project.dependencies.create(artifact)
cfg.dependencies.add(dep)
def files = cfg.resolve()
return getArtifacts(project, cfg, true)
}
private static boolean checkExists(url) {
try {
def code = new URL(url).openConnection().with {
requestMethod = 'HEAD'
connect()
responseCode
}
return code == 200
} catch (Exception e) {
if (e.toString().contains('unable to find valid certification path to requested target'))
throw new RuntimeException('Failed to connect to ' + url + ': Missing certificate root authority, try updating java')
throw e
}
}
static String getLatestForgeVersion(mcVersion) {
final json = new JsonSlurper().parseText(new URL('https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json').getText('UTF-8'))
final ver = json.promos["$mcVersion-latest"]
ver === null ? null : (mcVersion + '-' + ver)
}
static void processClassNodes(File file, Closure process) {
file.withInputStream { i ->
new ZipInputStream(i).withCloseable { zin ->
ZipEntry zein
while ((zein = zin.nextEntry) != null) {
if (zein.name.endsWith('.class')) {
def node = new ClassNode(ASM_LEVEL)
new ClassReader(zin).accept(node, 0)
process(node)
}
}
}
}
}
}
@@ -1,92 +0,0 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.forge.tasks
import net.minecraftforge.srgutils.MinecraftVersion
import org.gradle.api.DefaultTask
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.TaskAction
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.ClassNode
abstract class ValidateDeprecations extends DefaultTask {
@InputFile
abstract RegularFileProperty getInput()
@Input
abstract Property<String> getMcVersion()
ValidateDeprecations() {
this.onlyIf { !System.env.TEAMCITY_VERSION }
}
@TaskAction
protected void exec() {
def mcVer = MinecraftVersion.from(mcVersion.get())
def errors = []
Util.processClassNodes(input.get().asFile) {
processNode(mcVer, errors, it)
}
if (!errors.isEmpty()) {
errors.forEach {
this.logger.error("Deprecated ${it[0]} is marked for removal in ${it[1]} but is not yet removed")
}
throw new IllegalStateException("Found deprecated members marked for removal but not yet removed in ${mcVer}; see log for details")
}
}
protected processNode(MinecraftVersion mcVer, List<String> errors, ClassNode node) {
node.visibleAnnotations?.each { annotation ->
ValidateDeprecations.processAnnotations(annotation, mcVer, errors) {
"class ${node.name}"
}
}
node.fields?.each { field ->
field.visibleAnnotations?.each { annotation ->
ValidateDeprecations.processAnnotations(annotation, mcVer, errors) {
"field ${node.name}#${field.name}"
}
}
}
node.methods?.each { method ->
method.visibleAnnotations?.each { annotation ->
ValidateDeprecations.processAnnotations(annotation, mcVer, errors) {
"method ${node.name}#${method.name}${method.desc}"
}
}
}
}
private static void processAnnotations(AnnotationNode annotation, MinecraftVersion mcVer, List<String> errors, Closure context) {
def values = annotation.values
if (values === null)
return
int forRemoval = values.indexOf('forRemoval')
int since = values.indexOf('since')
if (annotation.desc == 'Ljava/lang/Deprecated;' && forRemoval !== -1 && since !== -1 && values.size() >= 4 && values[forRemoval + 1] === true) {
def oldVersion = MinecraftVersion.from(values[since + 1])
def split = ValidateDeprecations.splitDots(oldVersion.toString())
if (split.length < 2)
return
def removeVersion = MinecraftVersion.from("${split[0]}.${split[1] + 1}")
if (removeVersion <= mcVer)
errors.add([context(), removeVersion])
}
}
private static int[] splitDots(String version) {
String[] pts = version.split('\\.')
int[] values = new int[pts.length]
for (int x = 0; x < pts.length; x++)
values[x] = Integer.parseInt(pts[x])
return values
}
}
@@ -1,310 +0,0 @@
package net.minecraftforge.forge.tasks.checks
import groovy.transform.CompileStatic
import groovy.transform.TupleConstructor
import net.minecraftforge.forge.tasks.InheritanceData
import net.minecraftforge.srgutils.IMappingFile
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFile
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Optional
import org.objectweb.asm.Opcodes
@CompileStatic
abstract class CheckATs extends CheckTask {
@InputFile abstract RegularFileProperty getInheritance()
@InputFiles abstract ConfigurableFileCollection getAts()
@InputFile @Optional abstract RegularFileProperty getMappings()
@Override
void check(Reporter reporter, boolean fix) {
final IMappingFile mappings = mappings.map { RegularFile it -> IMappingFile.load(it.asFile) }.getOrNull()
final inheritance = InheritanceData.parse(this.inheritance.get().asFile)
ats.each {
final lines = process(it, reporter, inheritance)
if (fix) {
it.text = joinBack(lines, inheritance, mappings).join('\n')
}
}
}
private static TreeMap<String, ATParser.Entry> process(File file, Reporter reporter, Map<String, InheritanceData> inheritance) {
final TreeMap<String, ATParser.Entry> lines = ATParser.parse(file.readLines(), reporter)
final Map<String, ATParser.Entry> constructorGroups = [:]
final itr = lines.entrySet().iterator()
final toRemove = []
while (itr.hasNext()) {
final next = itr.next()
String key = next.key
final entry = next.value
if (entry === null) continue
final binaryName = entry.cls.replaceAll('\\.', '/')
// Process Groups, this will remove any entries outside the group that is covered by the group
if (entry.group) {
final jcls = inheritance[binaryName]
if (jcls === null) {
itr.remove()
reporter.report("Invalid group: $key")
} else if ('*' == entry.desc) {
if (!jcls.fields) {
itr.remove()
reporter.report("Invalid group, class has no fields: $key")
} else {
jcls.fields.each { field, value ->
final fkey = entry.cls + ' ' + field
if (accessLevel(value.access) < accessStr(entry.modifier)) {
if (lines.containsKey(fkey)) {
toRemove.add(fkey)
} else if (!entry.existing.contains(fkey)) {
reporter.report("Missing group entry: $fkey")
}
entry.children.add(fkey)
} else if (lines.containsKey(fkey)) {
toRemove.add(fkey)
reporter.report("Found invalid group entry: $fkey")
}
}
entry.existing.findAll { it !in entry.children }.each { println('Removed: ' + it) }
}
} else if ('*()' == entry.desc) {
if (!jcls.methods) {
itr.remove()
reporter.report("Invalid group, class has no methods: $key")
} else {
jcls.methods.each { mtd, value ->
if (mtd.startsWith('<clinit>') || mtd.startsWith('lambda$')) return
key = entry.cls + ' ' + mtd.replace(' ', '')
if (accessLevel(value.access) < accessStr(entry.modifier)) {
if (lines.containsKey(key)) {
toRemove.add(key)
} else if (!entry.existing.contains(key)) {
reporter.report("Missing group entry: $key")
}
entry.children.add(key)
} else if (lines.containsKey(key)) {
toRemove.add(key)
reporter.report("Found invalid group entry: $key")
}
}
entry.existing.findAll { it !in entry.children }.each { println('Removed: ' + it) }
}
} else if ('<init>' == entry.desc) { //Make all public non-abstract subclasses
constructorGroups.put(binaryName, entry)
}
}
// Process normal lines, remove invalid and remove narrowing
else {
def jcls = inheritance.get(binaryName)
if (jcls === null) {
itr.remove()
reporter.report("Invalid: $key")
} else if (entry.desc == '') {
if (accessLevel(jcls.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) {
itr.remove()
reporter.report("Invalid Narrowing: $key")
}
} else if (!entry.desc.contains('(')) {
if (!jcls.fields || !jcls.fields.containsKey(entry.desc)) {
itr.remove()
reporter.report("Invalid: $key")
} else {
final value = jcls.fields[entry.desc]
if (accessLevel(value.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) {
itr.remove()
reporter.report("Invalid Narrowing: $key - ${entry.comment}")
}
}
} else {
final jdesc = entry.desc.replace('(', ' (')
if (!jcls.methods || !jcls.methods.containsKey(jdesc)) {
itr.remove()
reporter.report("Invalid: $key")
} else {
final value = jcls.methods[jdesc]
if (accessLevel(value.access) > accessStr(entry.modifier) && (entry.comment === null || !entry.comment.startsWith('#force '))) {
itr.remove()
reporter.report("Invalid Narrowing: $key")
}
}
}
}
}
inheritance.each { tcls, value ->
if (!value.methods || ((value.access & Opcodes.ACC_ABSTRACT) !== 0)) return
String parent = tcls
while (parent !== null) {
constructorGroups[parent]?.tap { entry ->
value.methods.each { mtd, v ->
if (mtd.startsWith('<init>')) {
final child = tcls.replaceAll('/', '\\.') + ' ' + mtd.replace(' ', '')
if (accessLevel(v.access) < 3) {
if (lines.containsKey(child)) {
toRemove.add(child)
} else if (child !in entry.existing) {
reporter.report("Missing group entry: $child")
}
entry.children.add(child)
} else if (lines.containsKey(child)) {
toRemove.add(child)
reporter.report("Found invalid group entry: $child")
}
}
}
}
parent = inheritance[parent]?.superName
}
}
constructorGroups.values().each { entry -> entry.existing.findAll { it !in entry.children }.each{ reporter.report("Found invalid group entry: $it") } }
toRemove.each(lines.&remove)
return lines
}
private static List<String> joinBack(TreeMap<String, ATParser.Entry> lines, Map<String, InheritanceData> inheritance, IMappingFile mappings) {
final data = [] as List<String>
final remapComment = { ATParser.Entry entry ->
if (!mappings || !entry || !entry.desc) return null
final comment = entry.comment?.substring(1)?.trim()
final jsonCls = inheritance.get(entry.cls.replaceAll('\\.', '/'))
final mappingsClass = mappings?.getClass(jsonCls.name)
if (mappingsClass === null) return entry.comment
final idx = entry.desc.indexOf('(')
String mappedName = idx == -1
? mappingsClass.remapField(entry.desc)
: mappingsClass.remapMethod(entry.desc.substring(0, idx), entry.desc.substring(idx))
if (!mappedName) return entry.comment
if (mappedName == '<init>')
mappedName = 'constructor'
if (comment?.startsWith(mappedName))
return '# ' + comment
if (comment && comment.indexOf(' ') !== -1) {
def split = comment.split(' - ').toList()
if (split[0].indexOf(' ') !== -1)
// The first string is more than one word, so append before it
return "# ${mappedName} - ${comment}"
split.remove(0)
return "# ${mappedName} - ${String.join(' - ', split)}"
}
return '# ' + mappedName
}
lines.each { key, value ->
if (!value.group) {
def comment = remapComment.call(value)
data.add(value.modifier + ' ' + key + (comment ? ' ' + comment : ''))
} else {
data.add(('#group ' + value.modifier + ' ' + key + ' ' + (value.comment ?: '')).trim())
value.children.each {
final line = value.modifier + ' ' + it
final entry = ATParser.parseEntry(line)
final comment = remapComment(entry)
data.add(line + (comment ? ' ' + comment : ''))
}
data.add('#endgroup')
}
}
return data
}
static int accessStr(String access) {
if (access.endsWith('-f') || access.endsWith('+f')) return 4
switch (access.toLowerCase()) {
case 'public': return 3
case 'protected': return 2
case 'default': return 1
case 'private': return 0
default: return -1
}
}
static int accessLevel(int access) {
if ((access & Opcodes.ACC_PUBLIC) !== 0) return 3
if ((access & Opcodes.ACC_PROTECTED) !== 0) return 2
if ((access & Opcodes.ACC_PRIVATE) !== 0) return 0
return 1
}
}
@CompileStatic
class ATParser {
static TreeMap<String, Entry> parse(List<String> lines, CheckTask.Reporter reporter) {
TreeMap<String, Entry> outLines = new TreeMap<>()
Entry group = null
for (final line : lines) {
if (line.isEmpty()) continue
if (line.startsWith('#group ')) {
final entry = parseEntry(line.substring(7))
if (entry.desc != '*' && entry.desc != '*()' && entry.desc != '<init>') {
reporter.report("Invalid group: $line", false)
}
entry.group = true
entry.children = []
entry.existing = []
group = entry
if (outLines.containsKey(entry.key)) {
reporter.report("Duplicate group: $line", false)
}
outLines[entry.key] = group
} else if (group !== null) {
if (line.startsWith('#endgroup')) {
group = null
} else {
final key = parseEntry(line).key
group.existing.add(key)
}
} else if (line.startsWith('#endgroup')) {
reporter.report("Invalid group ending: $line", false)
} else if (line.startsWith('#')) {
//Nom
} else {
final entry = parseEntry(line)
if (outLines.containsKey(entry.key)) {
reporter.report("Found duplicate: $line")
continue
}
outLines[entry.key] = entry
}
}
return outLines
}
static Entry parseEntry(String line) {
final idx = line.indexOf('#')
final String comment = idx === -1 ? null : line.substring(idx)
if (idx !== -1) line = line.substring(0, idx - 1)
final data = (line.trim() + ' ').split(' ', -1)
new Entry(data[0], data[1], data[2], comment)
}
@TupleConstructor
static final class Entry {
String modifier, cls, desc, comment
Set<String> existing
TreeSet<String> children
boolean group = false
@Lazy
String key = {cls + (desc.isEmpty() ? '' : ' ' + desc)}()
Object getAt(String key) {
return getProperty(key)
}
}
}
@@ -1,93 +0,0 @@
package net.minecraftforge.forge.tasks.checks
import net.minecraftforge.forge.tasks.Util
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Type
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
abstract class CheckExcs extends CheckTask {
@InputFile abstract RegularFileProperty getBinary()
@InputFiles abstract ConfigurableFileCollection getExcs()
@Override
void check(Reporter reporter, boolean fix) {
final Set<String> known = []
collectKnown(known)
excs.each { f ->
final lines = []
f.eachLine { line ->
def idx = line.indexOf('#')
if (idx == 0 || line.isEmpty()) {
return
}
if (idx != -1) line = line.substring(0, idx - 1)
if (!line.contains('=')) {
reporter.report("Invalid: $line")
return
}
def (String key, String value) = line.split('=', 2)
if (!known.contains(key)) {
reporter.report("Unknown: $line")
return
}
String desc = key.split('\\.', 2)[1]
if (!desc.contains('(')) {
reporter.report("Invalid: $line")
return
}
desc = '(' + desc.split('\\(', 2)[1]
def (exceptions, String args) = value.contains('|') ? value.split('|', 2) : [value, '']
if (args.split(',').length !== Type.getArgumentTypes(desc).length) {
reporter.report("Invalid: $line")
return
}
lines.add(line)
}
if (fix) f.text = lines.sort().join('\n')
}
}
private void collectKnown(Collection<String> known) {
binary.get().asFile.withInputStream { i ->
new ZipInputStream(i).withCloseable { zin ->
final visitor = new ClassVisitor(Util.ASM_LEVEL) {
private String cls
@Override
void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.cls = name
}
@Override
MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
known.add(this.cls + '.' + name + descriptor)
super.visitMethod(access, name, descriptor, signature, exceptions)
}
}
ZipEntry zein
while ((zein = zin.nextEntry) !== null) {
if (zein.name.endsWith('.class')) {
ClassReader reader = new ClassReader(zin)
reader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES)
}
}
}
}
}
}
@@ -1,9 +0,0 @@
package net.minecraftforge.forge.tasks.checks
import groovy.transform.CompileStatic
@CompileStatic
enum CheckMode {
CHECK,
FIX
}
@@ -1,182 +0,0 @@
package net.minecraftforge.forge.tasks.checks
import groovy.transform.CompileStatic
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.Optional
import java.nio.file.Files
import java.nio.file.Path
import java.util.regex.Pattern
@CompileStatic
abstract class CheckPatches extends CheckTask {
private static final Pattern HUNK_START_PATTERN = Pattern.compile('^@@ -[0-9,]* \\+[0-9,_]* @@$')
private static final Pattern WHITESPACE_PATTERN = Pattern.compile('^[+\\-]\\s*$')
private static final Pattern IMPORT_PATTERN = Pattern.compile('^[+\\-]\\s*import.*')
private static final Pattern FIELD_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^=;]*)(=.*)?;\\s*$')
private static final Pattern METHOD_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final)?([^(]*)[(]([^)]*)?[)]\\s*[{]\\s*$')
private static final Pattern CLASS_PATTERN = Pattern.compile('^[+\\-][\\s]*((public|protected|private)[\\s]*)?(static[\\s]*)?(final[\\s]*)?(class|interface)([^{]*)[{]\\s*$')
private static final Map<String, Integer> ACCESS_MAP = [private: 0, protected: 2, public: 3].tap { it.put(null, 1) }
@InputDirectory abstract DirectoryProperty getPatchDir()
@Input @Optional abstract ListProperty<String> getPatchesWithS2SArtifact()
@Override
void check(Reporter reporter, boolean fix) {
final patchDir = getPatchDir().get().asFile.toPath()
Files.walk(patchDir).withCloseable {
it.filter(Files.&isRegularFile).forEach { path ->
final String relativeName = patchDir.relativize(path).toString()
verifyPatch(path, reporter, fix, relativeName, patchesWithS2SArtifact.get().contains(relativeName.replace('\\', '/')))
}
}
}
def accessChange(previous, current) {
//return ACCESS_MAP[previous] < ACCESS_MAP[current]
return previous != current
}
void verifyPatch(Path patch, Reporter reporter, boolean fix, String patchPath, boolean hasS2SArtifact) {
final oldFixedErrors = reporter.fixed.size()
final lines = Files.readAllLines(patch)
int hunksStart = 0
boolean onlyWhiteSpace = false
final List<String> newLines = []
// First two lines are file name ++/-- and we do not care
newLines.add(lines[0] + '\n')
newLines.add(lines[1] + '\n')
int i
for (i = 2; i < lines.size(); ++i) {
def line = lines[i]
newLines.add(line + '\n')
if (HUNK_START_PATTERN.matcher(line).find()) {
if (onlyWhiteSpace) {
if (!hasS2SArtifact)
reporter.report("Patch contains only white space hunk starting at line ${hunksStart + 1}, file: $patchPath")
int toRemove = i - hunksStart
while (toRemove-- > 0)
newLines.remove(newLines.size() - 1)
}
hunksStart = i
onlyWhiteSpace = true
continue
}
if (line.startsWithAny('+','-')) {
def prefixChange = false
def prevLine = lines[i - 1]
if (line.charAt(0) == (char)'+' && prevLine.charAt(0) == (char)'-') {
def prevTrim = prevLine.substring(1).replaceAll("\\s", "")
def currTrim = line.substring(1).replaceAll("\\s", "")
if (prevTrim == currTrim) {
prefixChange = true
}
def pMatcher = FIELD_PATTERN.matcher(prevLine)
def cMatcher = FIELD_PATTERN.matcher(line)
if (pMatcher.find() && cMatcher.find() &&
pMatcher.group(6) == cMatcher.group(6) && // = ...
pMatcher.group(5) == cMatcher.group(5) && // field name
pMatcher.group(3) == cMatcher.group(3) && // static
(accessChange(pMatcher.group(2), cMatcher.group(2)) || pMatcher.group(4) != cMatcher.group(4))) {
reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false)
}
pMatcher = METHOD_PATTERN.matcher(prevLine)
cMatcher = METHOD_PATTERN.matcher(line)
if (pMatcher.find() && cMatcher.find() &&
pMatcher.group(6) == cMatcher.group(6) && // params
pMatcher.group(5) == cMatcher.group(5) && // <T> void name
pMatcher.group(3) == cMatcher.group(3) && // static
(accessChange(pMatcher.group(2), cMatcher.group(2)) || pMatcher.group(4) != cMatcher.group(4))) {
reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false)
}
pMatcher = CLASS_PATTERN.matcher(prevLine)
cMatcher = CLASS_PATTERN.matcher(line)
if (pMatcher.find() && cMatcher.find() &&
pMatcher.group(6) == cMatcher.group(6) && // ClassName<> extends ...
pMatcher.group(5) == cMatcher.group(5) && // class | interface
pMatcher.group(3) == cMatcher.group(3) && // static
(accessChange(pMatcher.group(2), cMatcher.group(2)) || pMatcher.group(4) != cMatcher.group(4))) {
reporter.report("Patch contains access changes or final removal at line ${i + 1}, file: $patchPath", false)
}
}
if (line.charAt(0) == (char)'-' && i + 1 < lines.size()) {
final nextLine = lines[i + 1]
if (nextLine.charAt(0) == (char)'+') {
final nextTrim = nextLine.substring(1).replaceAll("\\s", "")
final currTrim = line.substring(1).replaceAll("\\s", "")
if (nextTrim == currTrim) {
prefixChange = true
}
}
}
final isWhiteSpaceChange = WHITESPACE_PATTERN.matcher(line).find()
if (!prefixChange && !isWhiteSpaceChange) {
onlyWhiteSpace = hasS2SArtifact && IMPORT_PATTERN.matcher(line).find()
} else if (isWhiteSpaceChange) {
final prevLineChange = prevLine.startsWithAny('+','-')
final nextLineChange = i + 1 < lines.size() && lines[i + 1].startsWithAny('+','-')
if (!prevLineChange && !nextLineChange) {
reporter.report("Patch contains white space change in valid hunk at line ${i + 1}, file: $patchPath", false)
}
}
if (line.contains('\t')) {
reporter.report("Patch contains tabs on line ${i + 1}, file: $patchPath")
line = line.replaceAll('\t', ' ')
newLines.remove(newLines.size() - 1)
newLines.add(line + '\n')
}
if (IMPORT_PATTERN.matcher(line).find() && !hasS2SArtifact) {
reporter.report("Patch contains import change on line ${i + 1}, file: $patchPath", false)
}
}
}
if (onlyWhiteSpace) {
if (!hasS2SArtifact)
reporter.report("Patch contains only white space hunk starting at line ${hunksStart + 1}, file: $patchPath")
def toRemove = i - hunksStart;
while (toRemove-- > 0)
newLines.remove(newLines.size() - 1)
}
if ((reporter.fixed.size() > oldFixedErrors && fix) || hasS2SArtifact) {
if (newLines.size() <= 2) {
logger.lifecycle("Patch is now empty removing, file: {}", patchPath)
Files.delete(patch)
}
else {
if (!hasS2SArtifact)
logger.lifecycle("*** Updating patch file. Please run setup then genPatches again. ***")
Files.newBufferedWriter(patch).withCloseable {
newLines.each { l -> it.write(l) }
}
}
}
}
}
@@ -1,110 +0,0 @@
package net.minecraftforge.forge.tasks.checks
import groovy.transform.CompileStatic
import net.minecraftforge.forge.tasks.Annotatable
import net.minecraftforge.forge.tasks.InheritanceData
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
@CompileStatic
abstract class CheckSAS extends CheckTask {
@InputFile abstract RegularFileProperty getInheritance()
@InputFiles abstract ConfigurableFileCollection getSass()
@Override
void check(Reporter reporter, boolean fix) {
final inheritance = InheritanceData.parse(this.inheritance.get().asFile)
sass.each { f ->
final lines = []
f.eachLine { line ->
if (line[0] == '\t') return // Skip any tabbed lines, those are ones we add
final idx = line.indexOf('#')
if (idx == 0 || line.isEmpty()) {
lines.add(line)
return
}
def comment = idx == -1 ? null : line.substring(idx)
if (idx != -1) line = line.substring(0, idx - 1)
final spl = (line.trim().replace('(', ' (') + ' ').split(' ', -1)
def (String cls, String name, String desc) = [spl[0], spl[1], spl[2]]
cls = cls.replaceAll('\\.', '/')
if (inheritance[cls] === null) {
reporter.report("Invalid: $line")
} else if (name.isEmpty()) { //Class SAS
final toAdd = []
final clsSided = isSided(inheritance[cls])
boolean sided = clsSided
/* TODO: MergeTool doesn't do fields
if (json[cls]['fields'] != null) {
for (entry in json[cls]['fields']) {
if (isSided(entry.value)) {
sided = true
toAdd.add('\t' + cls + ' ' + entry.key)
}
}
} */
final clsInh = inheritance[cls]
if (clsInh.methods) {
for (entry in clsInh.methods) {
if (isSided(entry.value)) {
sided = true
toAdd.add('\t' + cls + ' ' + entry.key.replaceAll(' ', ''))
findChildMethods(inheritance, cls, entry.key).each { lines.add('\t' + it) }
findChildMethods(inheritance, cls, entry.key).each { println(line + ' -- ' + it) }
} else if (clsSided) {
findChildMethods(inheritance, cls, entry.key).each { lines.add('\t' + it) }
findChildMethods(inheritance, cls, entry.key).each { println(line + ' -- ' + it) }
}
}
}
if (sided) {
lines.add(cls + (comment == null ? '' : ' ' + comment))
lines.addAll(toAdd.sort())
} else {
reporter.report("Invalid: $line")
}
} else if (desc.isEmpty()) { // Fields
/* TODO: MergeTool doesn't do fields
if (json[cls]['fields'] != null && isSided(json[cls]['fields'][name]))
lines.add(cls + ' ' + name + (comment == null ? '' : ' ' + comment))
else */
reporter.report("Invalid: $line")
} else { // Methods
final clsInh = inheritance[cls]
if (clsInh.methods === null || !isSided(clsInh.methods[name + ' ' + desc]))
reporter.report("Invalid: $line")
else {
lines.add(cls + ' ' + name + desc + (comment == null ? '' : ' ' + comment))
findChildMethods(inheritance, cls, name + ' ' + desc).each { println(line + ' -- ' + it) }
findChildMethods(inheritance, cls, name + ' ' + desc).each { lines.add('\t' + it) }
}
}
}
if (fix) f.text = lines.join('\n')
}
}
protected static isSided(Annotatable annotatable) {
if (annotatable === null) return false
for (ann in annotatable.annotations) {
if ('Lnet/minecraftforge/api/distmarker/OnlyIn;' == ann.desc)
return true
}
return false
}
protected static findChildMethods(Map<String, InheritanceData> json, String cls, String desc) {
return json.values().findAll{ it.methods != null && it.methods[desc] != null && it.methods[desc].override == cls && isSided(it.methods[desc]) }
.collect { it.name + ' ' + desc.replace(' ', '') } as TreeSet
}
}
@@ -1,111 +0,0 @@
package net.minecraftforge.forge.tasks.checks
import groovy.transform.CompileStatic
import net.minecraftforge.forge.tasks.Util
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.logging.LogLevel
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.TaskContainer
import org.gradle.api.tasks.VerificationTask
@CompileStatic
abstract class CheckTask extends DefaultTask implements VerificationTask {
@Input
abstract Property<CheckMode> getMode()
private boolean ignoreFailures = false
@Input
@Override
boolean getIgnoreFailures() {
return ignoreFailures
}
@Override
void setIgnoreFailures(boolean ignoreFailures) {
this.ignoreFailures = ignoreFailures
}
@TaskAction
void run() {
Util.init()
final doFix = getMode().get() === CheckMode.FIX
final Reporter reporter = new Reporter(doFix)
check(reporter, doFix)
if (reporter.messages) {
if (getMode().get() === CheckMode.CHECK) {
logger.error("Check task '{}' found errors:\n{}", name, reporter.messages.join('\n'))
if (!ignoreFailures) {
throw new IllegalArgumentException("${reporter.messages.size()} errors were found!")
}
} else {
if (logger.isEnabled(LogLevel.DEBUG)) {
logger.warn("Check task '{}' found {} errors and fixed {}:\n{}", name, reporter.messages.size(), reporter.fixed.size(), reporter.fixed.join('\n'))
} else {
logger.warn("Check task '{}' found {} errors and fixed {}.", name, reporter.messages.size(), reporter.fixed.size())
}
if (reporter.notFixed) {
logger.error('{} errors could not be fixed:\n{}', reporter.notFixed.size(), reporter.notFixed.join('\n'))
if (!ignoreFailures) {
throw new IllegalArgumentException("${reporter.notFixed.size()} errors which cannot be fixed were found!")
}
}
}
}
}
abstract void check(Reporter reporter, boolean fix)
@CompileStatic
static final class Reporter {
final boolean trackFixed
Reporter(boolean trackFixed) {
this.trackFixed = trackFixed
}
public final List<String> messages = []
public final List<String> fixed = []
public final List<String> notFixed = []
void report(String message, boolean canBeFixed = true) {
messages.add(message)
if (trackFixed) {
if (canBeFixed) {
fixed.add(message)
} else {
notFixed.add(message)
}
}
}
}
static <T extends CheckTask> void registerTask(TaskContainer tasks, String taskName, @DelegatesTo.Target('type') Class<T> clazz,
@DelegatesTo(genericTypeIndex = 0, target = 'type') Closure configuration) {
taskName = taskName.capitalize()
tasks.register("check$taskName", clazz) { CheckTask task ->
def castedTask = task as T
configuration.setDelegate(castedTask)
configuration.call(castedTask)
castedTask.mode.set(CheckMode.CHECK)
castedTask.group = 'checks'
}
tasks.named('checkAll').configure { it.dependsOn("check$taskName") }
tasks.register("checkAndFix$taskName", clazz) { CheckTask task ->
def castedTask = task as T
configuration.setDelegate(castedTask)
configuration.call(castedTask)
castedTask.mode.set(CheckMode.FIX)
castedTask.group = 'checks'
}
tasks.named('checkAllAndFix').configure { it.dependsOn("checkAndFix$taskName") }
}
}
+113
View File
@@ -0,0 +1,113 @@
import os, os.path, sys, re
import json, urllib2
from pprint import pprint
from urlparse import urlparse
import base64
import optparse, ast
class PreemptiveBasicAuthHandler(urllib2.BaseHandler):
def __init__(self, password_mgr=None):
if password_mgr is None:
password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
self.passwd = password_mgr
self.add_password = self.passwd.add_password
def http_request(self,req):
uri = req.get_full_url()
user, pw = self.passwd.find_user_password(None,uri)
if pw is None: return req
raw = "%s:%s" % (user, pw)
auth = 'Basic %s' % base64.b64encode(raw).strip()
req.add_unredirected_header('Authorization', auth)
return req
def https_request(self,req):
return self.http_request(req)
def read_url(url):
handler = PreemptiveBasicAuthHandler()
handler.add_password(None, 'ci.jenkins.minecraftforge.net', 'console_script', 'dc6d48ca20a474beeac280a9a16a926e')
file = urllib2.build_opener(handler).open(url)
data = file.read()
file.close()
return data
def getBuildInfo(url, current_version=None):
data = read_url(url)
data = ast.literal_eval(data)['allBuilds']
data = sorted(data, key=lambda key: key['number'], reverse=False)
items = []
output = []
for build in data:
build['actions'] = filter(lambda act: act is not None, build['actions'])
build['actions'] = filter(lambda act: 'text' in act, build['actions'])
build['actions'] = filter(lambda act: not ' ' in act['text'], build['actions'])
if len(build['actions']) == 0:
build['version'] = current_version
current_version = None
else:
build['version'] = build['actions'][0]['text']
build['items'] = build['changeSet']['items']
for item in build['items']:
item['author'] = item['author']['fullName']
if build['result'] != 'SUCCESS':
items += build['items']
else:
build['items'] = items + build['items']
items = []
output += [build]
build.pop('changeSet')
build.pop('actions')
return sorted(output, key=lambda key: key['number'], reverse=True)
def add_latest_build(url, builds, current_version=None):
data = read_url(url)
data = ast.literal_eval(data)
number = data['number']
if builds[0]['number'] == data['number']:
return builds
for item in data['changeSet']['items']:
item['author'] = item['author']['fullName']
build = {
'number' : data['number'],
'result' : 'SUCCESS', #Currently build should always be success... Else things derp after words
'version' : current_version,
'items' : data['changeSet']['items']
}
return [build] + builds
def make_changelog(job_path, target_build, change_file, current_version=None):
builds = getBuildInfo('%s/api/python?tree=allBuilds[result,number,actions[text],changeSet[items[author[fullName],comment]]]&pretty=true' % job_path, current_version)
builds = add_latest_build('%s/lastBuild/api/python?pretty=true&tree=number,changeSet[items[author[fullName],comment]]' % job_path, builds, current_version)
log = [ "Changelog:" ]
for build in builds:
if int(build['number']) > target_build: continue
if len(build['items']) == 0: continue
log.append('')
if build['version'] is None:
log.append('Build %s' % build['number'])
else:
log.append('Build %s' % build['version'])
for change in build['items']:
comments = filter(lambda cmt: len(cmt) > 0, change['comment'].split('\n'))
if len(comments) > 1:
log.append('\t' + change['author'])
for comment in comments:
log.append('\t\t' + comment)
elif len(comments) == 1:
log.append('\t%s: %s' % (change['author'], comments[0]))
file = open(change_file, 'wb')
for line in log:
file.write('%s\n' % line)
file.close()
if __name__ == '__main__':
make_changelog("http://ci.jenkins.minecraftforge.net/job/minecraftforge/", 70000, 'changelog.txt', 'pinecone')
Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+946
View File
@@ -0,0 +1,946 @@
package ibxm;
public class Channel {
public int pattern_loop_row;
private Module module;
private Instrument instrument;
private Sample sample;
private int[] global_volume, current_note;
private boolean linear_periods, fast_volume_slides, key_on, silent;
private int sample_idx, sample_frac, step, left_gain, right_gain;
private int volume, panning, fine_tune, period, porta_period, key_add;
private int tremolo_speed, tremolo_depth, tremolo_tick, tremolo_wave, tremolo_add;
private int vibrato_speed, vibrato_depth, vibrato_tick, vibrato_wave, vibrato_add;
private int volume_slide_param, portamento_param, retrig_param;
private int volume_envelope_tick, panning_envelope_tick;
private int effect_tick, trigger_tick, fade_out_volume, random_seed;
private int log_2_sampling_rate;
private static final int LOG_2_29024 = LogTable.log_2( 29024 );
private static final int LOG_2_8287 = LogTable.log_2( 8287 );
private static final int LOG_2_8363 = LogTable.log_2( 8363 );
private static final int LOG_2_1712 = LogTable.log_2( 1712 );
private static final int[] sine_table = new int[] {
0, 24 , 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, 235, 244, 250, 253,
255, 253, 250, 244, 235, 224, 212, 197, 180, 161, 141, 120, 97, 74, 49, 24
};
public Channel( Module mod, int sampling_rate, int[] global_vol ) {
module = mod;
global_volume = global_vol;
linear_periods = module.linear_periods;
fast_volume_slides = module.fast_volume_slides;
current_note = new int[ 5 ];
log_2_sampling_rate = LogTable.log_2( sampling_rate );
}
public void reset() {
tremolo_speed = 0;
tremolo_depth = 0;
tremolo_wave = 0;
vibrato_speed = 0;
vibrato_depth = 0;
vibrato_wave = 0;
volume_slide_param = 0;
portamento_param = 0;
retrig_param = 0;
random_seed = 0xABC123;
instrument = module.get_instrument( 0 );
row( 48, 256, 0, 0, 0 );
}
public void resample( int[] mixing_buffer, int frame_offset, int frames, int quality ) {
if( !silent ) {
switch( quality ) {
default:
sample.resample_nearest( sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames );
break;
case 1:
sample.resample_linear( sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames );
break;
case 2:
sample.resample_sinc( sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames );
break;
}
}
}
public void update_sample_idx( int samples ) {
sample_frac += step * samples;
sample_idx += sample_frac >> IBXM.FP_SHIFT;
sample_frac &= IBXM.FP_MASK;
}
public void set_volume( int vol ) {
if( vol < 0 ) {
vol = 0;
}
if( vol > 64 ) {
vol = 64;
}
volume = vol;
}
public void set_panning( int pan ) {
if( pan < 0 ) {
pan = 0;
}
if( pan > 255 ) {
pan = 255;
}
panning = pan;
}
public void row( int key, int inst_idx, int volume_column, int effect, int effect_param ) {
effect = effect & 0xFF;
if( effect >= 0x30 ) {
/* Effects above 0x30 are internal.*/
effect = 0;
}
if( effect == 0x00 && effect_param != 0 ) {
/* Arpeggio.*/
effect = 0x40;
}
if( effect == 0x0E ) {
/* Renumber 0x0Ex effect command.*/
effect = 0x30 + ( ( effect_param & 0xF0 ) >> 4 );
effect_param = effect_param & 0x0F;
}
if( effect == 0x21 ) {
/* Renumber 0x21x effect command.*/
effect = 0x40 + ( ( effect_param & 0xF0 ) >> 4 );
effect_param = effect_param & 0x0F;
}
current_note[ 0 ] = key;
current_note[ 1 ] = inst_idx;
current_note[ 2 ] = volume_column;
current_note[ 3 ] = effect;
current_note[ 4 ] = effect_param;
effect_tick = 0;
trigger_tick += 1;
update_envelopes();
key_add = 0;
vibrato_add = 0;
tremolo_add = 0;
if( ! ( effect == 0x3D && effect_param > 0 ) ) {
/* Not note delay.*/
trigger( key, inst_idx, volume_column, effect );
/* Handle volume column.*/
switch( volume_column & 0xF0 ) {
case 0x00:
/* Do nothing.*/
break;
case 0x60:
/* Volume slide down.*/
break;
case 0x70:
/* Volume slide up.*/
break;
case 0x80:
/* Fine volume slide down.*/
set_volume( volume - ( volume_column & 0x0F ) );
break;
case 0x90:
/* Fine volume slide up.*/
set_volume( volume + ( volume_column & 0x0F ) );
break;
case 0xA0:
/* Set vibrato speed.*/
set_vibrato_speed( volume_column & 0x0F );
break;
case 0xB0:
/* Vibrato.*/
set_vibrato_depth( volume_column & 0x0F );
vibrato();
break;
case 0xC0:
/* Set panning.*/
set_panning( ( volume_column & 0x0F ) << 4 );
break;
case 0xD0:
/* Panning slide left.*/
break;
case 0xE0:
/* Panning slide right.*/
break;
case 0xF0:
/* Tone portamento.*/
set_portamento_param( volume_column & 0x0F );
break;
default:
/* Set volume.*/
set_volume( volume_column - 0x10 );
break;
}
}
if( instrument.vibrato_depth > 0 ) {
auto_vibrato();
}
switch( effect ) {
case 0x01:
/* Portmento Up.*/
set_portamento_param( effect_param );
portamento_up();
break;
case 0x02:
/* Portamento Down.*/
set_portamento_param( effect_param );
portamento_down();
break;
case 0x03:
/* Tone Portamento.*/
set_portamento_param( effect_param );
break;
case 0x04:
/* Vibrato.*/
set_vibrato_speed( ( effect_param & 0xF0 ) >> 4 );
set_vibrato_depth( effect_param & 0x0F );
vibrato();
break;
case 0x05:
/* Tone Portamento + Volume Slide.*/
set_volume_slide_param( effect_param );
volume_slide();
break;
case 0x06:
/* Vibrato + Volume Slide.*/
set_volume_slide_param( effect_param );
vibrato();
volume_slide();
break;
case 0x07:
/* Tremolo.*/
set_tremolo_speed( ( effect_param & 0xF0 ) >> 4 );
set_tremolo_depth( effect_param & 0x0F );
tremolo();
break;
case 0x08:
/* Set Panning.*/
set_panning( effect_param );
break;
case 0x09:
/* Set Sample Index.*/
set_sample_index( effect_param << 8 );
break;
case 0x0A:
/* Volume Slide.*/
set_volume_slide_param( effect_param );
volume_slide();
break;
case 0x0B:
/* Pattern Jump.*/
break;
case 0x0C:
/* Set volume.*/
set_volume( effect_param );
break;
case 0x0D:
/* Pattern Break.*/
break;
case 0x0E:
/* Extended Commands (See 0x30-0x3F).*/
break;
case 0x0F:
/* Set Speed/Tempo.*/
break;
case 0x10:
/* Set Global Volume.*/
set_global_volume( effect_param );
break;
case 0x11:
/* global Volume Slide.*/
set_volume_slide_param( effect_param );
break;
case 0x14:
/* Key Off*/
if( effect_param == 0 ) {
key_on = false;
}
break;
case 0x15:
/* Set Envelope Tick.*/
set_envelope_tick( effect_param );
break;
case 0x19:
/* Panning Slide.*/
set_volume_slide_param( effect_param );
break;
case 0x1B:
/* Retrig + Volume Slide.*/
set_retrig_param( effect_param );
retrig_volume_slide();
break;
case 0x1D:
/* Tremor.*/
set_retrig_param( effect_param );
tremor();
break;
case 0x24:
/* S3M Fine Vibrato.*/
set_vibrato_speed( ( effect_param & 0xF0 ) >> 4 );
set_vibrato_depth( effect_param & 0x0F );
fine_vibrato();
break;
case 0x25:
/* S3M Set Speed.*/
break;
case 0x30:
/* Amiga Set Filter.*/
break;
case 0x31:
/* Fine Portamento Up.*/
set_portamento_param( 0xF0 | effect_param );
portamento_up();
break;
case 0x32:
/* Fine Portamento Down.*/
set_portamento_param( 0xF0 | effect_param );
portamento_down();
break;
case 0x33:
/* Set Glissando Mode.*/
break;
case 0x34:
/* Set Vibrato Waveform.*/
set_vibrato_wave( effect_param );
break;
case 0x35:
/* Set Fine Tune.*/
break;
case 0x36:
/* Pattern Loop.*/
break;
case 0x37:
/* Set Tremolo Waveform.*/
set_tremolo_wave( effect_param );
break;
case 0x38:
/* Set Panning(Obsolete).*/
break;
case 0x39:
/* Retrig.*/
set_retrig_param( effect_param );
break;
case 0x3A:
/* Fine Volume Slide Up.*/
set_volume_slide_param( ( effect_param << 4 ) | 0x0F );
volume_slide();
break;
case 0x3B:
/* Fine Volume Slide Down.*/
set_volume_slide_param( 0xF0 | effect_param );
volume_slide();
break;
case 0x3C:
/* Note Cut.*/
if( effect_param == 0 ) {
set_volume( 0 );
}
break;
case 0x3D:
/* Note Delay.*/
break;
case 0x3E:
/* Pattern Delay.*/
break;
case 0x3F:
/* Invert Loop.*/
break;
case 0x40:
/* Arpeggio.*/
break;
case 0x41:
/* Extra Fine Porta Up.*/
set_portamento_param( 0xE0 | effect_param );
portamento_up();
break;
case 0x42:
/* Extra Fine Porta Down.*/
set_portamento_param( 0xE0 | effect_param );
portamento_down();
break;
}
calculate_amplitude();
calculate_frequency();
}
public void tick() {
int volume_column, effect, effect_param;
volume_column = current_note[ 2 ];
effect = current_note[ 3 ];
effect_param = current_note[ 4 ];
effect_tick += 1;
if( effect == 0x3D && effect_param == effect_tick ) {
/* Note delay.*/
row( current_note[ 0 ], current_note[ 1 ], volume_column, 0, 0 );
} else {
trigger_tick += 1;
vibrato_tick += 1;
tremolo_tick += 1;
update_envelopes();
key_add = 0;
vibrato_add = 0;
tremolo_add = 0;
if( instrument.vibrato_depth > 0 ) {
auto_vibrato();
}
switch( volume_column & 0xF0 ) {
case 0x60:
/* Volume Slide Down.*/
set_volume( volume - ( volume_column & 0x0F ) );
break;
case 0x70:
/* Volume Slide Up.*/
set_volume( volume + ( volume_column & 0x0F ) );
break;
case 0xB0:
/* Vibrato.*/
vibrato();
break;
case 0xD0:
/* Panning Slide Left.*/
set_panning( panning - ( volume_column & 0x0F ) );
break;
case 0xE0:
/* Panning Slide Right.*/
set_panning( panning + ( volume_column & 0x0F ) );
break;
case 0xF0:
/* Tone Portamento.*/
tone_portamento();
break;
}
switch( effect ) {
case 0x01:
/* Portamento Up.*/
portamento_up();
break;
case 0x02:
/* Portamento Down.*/
portamento_down();
break;
case 0x03:
/* Tone Portamento.*/
tone_portamento();
break;
case 0x04:
/* Vibrato.*/
vibrato();
break;
case 0x05:
/* Tone Portamento + Volume Slide.*/
tone_portamento();
volume_slide();
break;
case 0x06:
/* Vibrato + Volume Slide */
vibrato();
volume_slide();
break;
case 0x07:
/* Tremolo.*/
tremolo();
break;
case 0x0A:
/* Volume Slide.*/
volume_slide();
break;
case 0x11:
/* Global Volume Slide.*/
global_volume_slide();
break;
case 0x14:
/* Key off.*/
if( effect_tick == effect_param ) {
key_on = false;
}
break;
case 0x19:
/* Panning Slide.*/
panning_slide();
break;
case 0x1B:
/* Retrig + Volume Slide.*/
retrig_volume_slide();
break;
case 0x1D:
/* Tremor.*/
tremor();
break;
case 0x24:
/* S3M Fine Vibrato.*/
fine_vibrato();
break;
case 0x39:
/* Retrig.*/
retrig_volume_slide();
break;
case 0x3C:
/* Note Cut.*/
if( effect_tick == effect_param ) {
set_volume( 0 );
}
break;
case 0x40:
/* Arpeggio.*/
switch( effect_tick % 3 ) {
case 1:
key_add = ( effect_param & 0xF0 ) >> 4;
break;
case 2:
key_add = effect_param & 0x0F;
break;
}
break;
}
}
calculate_amplitude();
calculate_frequency();
}
private void set_vibrato_speed( int speed ) {
if( speed > 0 ) {
vibrato_speed = speed;
}
}
private void set_vibrato_depth( int depth ) {
if( depth > 0 ) {
vibrato_depth = depth;
}
}
private void set_vibrato_wave( int wave ) {
if( wave < 0 || wave > 7 ) {
wave = 0;
}
vibrato_wave = wave;
}
private void set_tremolo_speed( int speed ) {
if( speed > 0 ) {
tremolo_speed = speed;
}
}
private void set_tremolo_depth( int depth ) {
if( depth > 0 ) {
tremolo_depth = depth;
}
}
private void set_tremolo_wave( int wave ) {
if( wave < 0 || wave > 7 ) {
wave = 0;
}
tremolo_wave = wave;
}
private void vibrato() {
int vibrato_phase;
vibrato_phase = vibrato_tick * vibrato_speed;
vibrato_add += waveform( vibrato_phase, vibrato_wave ) * vibrato_depth >> 5;
}
private void fine_vibrato() {
int vibrato_phase;
vibrato_phase = vibrato_tick * vibrato_speed;
vibrato_add += waveform( vibrato_phase, vibrato_wave ) * vibrato_depth >> 7;
}
private void tremolo() {
int tremolo_phase;
tremolo_phase = tremolo_tick * tremolo_speed;
tremolo_add += waveform( tremolo_phase, tremolo_wave ) * tremolo_depth >> 6;
}
private void set_portamento_param( int param ) {
if( param != 0 ) {
portamento_param = param;
}
}
private void tone_portamento() {
int new_period;
if( porta_period < period ) {
new_period = period - ( portamento_param << 2 );
if( new_period < porta_period ) {
new_period = porta_period;
}
set_period( new_period );
}
if( porta_period > period ) {
new_period = period + ( portamento_param << 2 );
if( new_period > porta_period ) {
new_period = porta_period;
}
set_period( new_period );
}
}
private void portamento_up() {
if( ( portamento_param & 0xF0 ) == 0xE0 ) {
/* Extra-fine porta.*/
if( effect_tick == 0 ) {
set_period( period - ( portamento_param & 0x0F ) );
}
} else if( ( portamento_param & 0xF0 ) == 0xF0 ) {
/* Fine porta.*/
if( effect_tick == 0 ) {
set_period( period - ( ( portamento_param & 0x0F ) << 2 ) );
}
} else {
/* Normal porta.*/
if( effect_tick > 0 ) {
set_period( period - ( portamento_param << 2 ) );
}
}
}
private void portamento_down() {
if( ( portamento_param & 0xF0 ) == 0xE0 ) {
/* Extra-fine porta.*/
if( effect_tick == 0 ) {
set_period( period + ( portamento_param & 0x0F ) );
}
} else if( ( portamento_param & 0xF0 ) == 0xF0 ) {
/* Fine porta.*/
if( effect_tick == 0 ) {
set_period( period + ( ( portamento_param & 0x0F ) << 2 ) );
}
} else {
/* Normal porta.*/
if( effect_tick > 0 ) {
set_period( period + ( portamento_param << 2 ) );
}
}
}
private void set_period( int p ) {
if( p < 32 ) {
p = 32;
}
if( p > 32768 ) {
p = 32768;
}
period = p;
}
private void set_global_volume( int vol ) {
if( vol < 0 ) {
vol = 0;
}
if( vol > 64 ) {
vol = 64;
}
global_volume[ 0 ] = vol;
}
private void set_volume_slide_param( int param ) {
if( param != 0 ) {
volume_slide_param = param;
}
}
private void global_volume_slide() {
int up, down;
up = ( volume_slide_param & 0xF0 ) >> 4;
down = volume_slide_param & 0x0F;
set_global_volume( global_volume[ 0 ] + up - down );
}
private void volume_slide() {
int up, down;
up = ( volume_slide_param & 0xF0 ) >> 4;
down = volume_slide_param & 0x0F;
if( down == 0x0F && up > 0 ) {
/* Fine slide up.*/
if( effect_tick == 0 ) {
set_volume( volume + up );
}
} else if( up == 0x0F && down > 0 ) {
/* Fine slide down.*/
if( effect_tick == 0 ) {
set_volume( volume - down );
}
} else {
/* Normal slide.*/
if( effect_tick > 0 || fast_volume_slides ) {
set_volume( volume + up - down );
}
}
}
private void panning_slide() {
int left, right;
left = ( volume_slide_param & 0xF0 ) >> 4;
right = volume_slide_param & 0x0F;
set_panning( panning - left + right );
}
private void set_retrig_param( int param ) {
if( param != 0 ) {
retrig_param = param;
}
}
private void tremor() {
int on_ticks, cycle_length, cycle_index;
on_ticks = ( ( retrig_param & 0xF0 ) >> 4 ) + 1;
cycle_length = on_ticks + ( retrig_param & 0x0F ) + 1;
cycle_index = trigger_tick % cycle_length;
if( cycle_index >= on_ticks ) {
tremolo_add = -64;
}
}
private void retrig_volume_slide() {
int retrig_volume, retrig_tick;
retrig_volume = ( retrig_param & 0xF0 ) >> 4;
retrig_tick = retrig_param & 0x0F;
if( retrig_tick > 0 && ( trigger_tick % retrig_tick ) == 0 ) {
set_sample_index( 0 );
switch( retrig_volume ) {
case 0x01:
set_volume( volume - 1 );
break;
case 0x02:
set_volume( volume - 2 );
break;
case 0x03:
set_volume( volume - 4 );
break;
case 0x04:
set_volume( volume - 8 );
break;
case 0x05:
set_volume( volume - 16 );
break;
case 0x06:
set_volume( volume - volume / 3 );
break;
case 0x07:
set_volume( volume / 2 );
break;
case 0x09:
set_volume( volume + 1 );
break;
case 0x0A:
set_volume( volume + 2 );
break;
case 0x0B:
set_volume( volume + 4 );
break;
case 0x0C:
set_volume( volume + 8 );
break;
case 0x0D:
set_volume( volume + 16 );
break;
case 0x0E:
set_volume( volume + volume / 2 );
break;
case 0x0F:
set_volume( volume * 2 );
break;
}
}
}
private void set_sample_index( int index ) {
if( index < 0 ) {
index = 0;
}
sample_idx = index;
sample_frac = 0;
}
private void set_envelope_tick( int tick ) {
volume_envelope_tick = tick;
panning_envelope_tick = tick;
}
private void trigger( int key, int instrument_idx, int volume_column, int effect ) {
if( instrument_idx > 0 ) {
instrument = module.get_instrument( instrument_idx );
sample = instrument.get_sample_from_key( key );
set_volume( sample.volume );
if( sample.set_panning ) {
set_panning( sample.panning );
}
set_envelope_tick( 0 );
fade_out_volume = 32768;
key_on = true;
}
if( key > 0 ) {
if( key < 97 ) {
porta_period = key_to_period( key );
if( effect != 0x03 && effect != 0x05 ) {
if( ( volume_column & 0xF0 ) != 0xF0 ) {
/* Not portamento.*/
trigger_tick = 0;
if( vibrato_wave < 4 ) {
vibrato_tick = 0;
}
if( tremolo_wave < 4 ) {
tremolo_tick = 0;
}
set_period( porta_period );
set_sample_index( 0 );
}
}
} else {
/* Key off.*/
key_on = false;
}
}
}
private void update_envelopes() {
Envelope envelope;
if( instrument.volume_envelope_active ) {
if( !key_on ) {
fade_out_volume -= instrument.volume_fade_out & 0xFFFF;
if( fade_out_volume < 0 ) {
fade_out_volume = 0;
}
}
envelope = instrument.get_volume_envelope();
volume_envelope_tick = envelope.next_tick( volume_envelope_tick, key_on );
}
if( instrument.panning_envelope_active ) {
envelope = instrument.get_panning_envelope();
panning_envelope_tick = envelope.next_tick( panning_envelope_tick, key_on );
}
}
private void auto_vibrato() {
int sweep, depth, rate;
sweep = instrument.vibrato_sweep & 0xFF;
depth = instrument.vibrato_depth & 0x0F;
rate = instrument.vibrato_rate & 0x3F;
if( trigger_tick < sweep ) {
depth = depth * trigger_tick / sweep;
}
vibrato_add += waveform( trigger_tick * rate, 0 ) * depth >> 9;
}
private int waveform( int phase, int wform ) {
int amplitude;
amplitude = 0;
switch( wform & 0x3 ) {
case 0:
/* Sine. */
if( ( phase & 0x20 ) == 0 ) {
amplitude = sine_table[ phase & 0x1F ];
} else {
amplitude = -sine_table[ phase & 0x1F ];
}
break;
case 1:
/* Saw. */
if( ( phase & 0x20 ) == 0 ) {
amplitude = ( phase & 0x1F ) << 3;
} else {
amplitude = ( ( phase & 0x1F ) << 3 ) - 255;
}
break;
case 2:
/* Square. */
if( ( phase & 0x20 ) == 0 ) {
amplitude = 255;
} else {
amplitude = -255;
}
break;
case 3:
/* Random. */
amplitude = ( random_seed >> 15 ) - 255;
random_seed = ( random_seed * 65 + 17 ) & 0xFFFFFF;
break;
}
return amplitude;
}
private int key_to_period( int key ) {
int octave, log_2_period, period_out;
octave = ( key << IBXM.FP_SHIFT ) / 12 + sample.transpose;
if( linear_periods ) {
period_out = 7744 - ( octave * 768 >> IBXM.FP_SHIFT );
} else {
log_2_period = LOG_2_29024 - octave;
period_out = LogTable.raise_2( log_2_period );
period_out = period_out >> ( IBXM.FP_SHIFT - 1 );
period_out = ( period_out >> 1 ) + ( period_out & 1 );
}
return period_out;
}
private void calculate_amplitude() {
int envelope_volume, tremolo_volume, amplitude;
int envelope_panning, mixer_panning, panning_range;
Envelope envelope;
envelope_volume = 0;
if( instrument.volume_envelope_active ) {
envelope = instrument.get_volume_envelope();
envelope_volume = envelope.calculate_ampl( volume_envelope_tick );
} else {
if( key_on ) {
envelope_volume = 64;
}
}
tremolo_volume = volume + tremolo_add;
if( tremolo_volume < 0 ) {
tremolo_volume = 0;
}
if( tremolo_volume > 64 ) {
tremolo_volume = 64;
}
amplitude = tremolo_volume << IBXM.FP_SHIFT - 6;
amplitude = amplitude * envelope_volume >> 6;
amplitude = amplitude * fade_out_volume >> 15;
amplitude = amplitude * global_volume[ 0 ] >> 6;
amplitude = amplitude * module.channel_gain >> IBXM.FP_SHIFT;
silent = sample.has_finished( sample_idx );
if( amplitude <= 0 ) {
silent = true;
} else {
envelope_panning = 32;
if( instrument.panning_envelope_active ) {
envelope = instrument.get_panning_envelope();
envelope_panning = envelope.calculate_ampl( panning_envelope_tick );
}
mixer_panning = ( panning & 0xFF ) << IBXM.FP_SHIFT - 8;
panning_range = IBXM.FP_ONE - mixer_panning;
if( panning_range > mixer_panning ) {
panning_range = mixer_panning;
}
mixer_panning = mixer_panning + ( panning_range * ( envelope_panning - 32 ) >> 5 );
left_gain = amplitude * ( IBXM.FP_ONE - mixer_panning ) >> IBXM.FP_SHIFT;
right_gain = amplitude * mixer_panning >> IBXM.FP_SHIFT;
}
}
private void calculate_frequency() {
int vibrato_period, log_2_freq;
vibrato_period = period + vibrato_add;
if( vibrato_period < 32 ) {
vibrato_period = 32;
}
if( vibrato_period > 32768 ) {
vibrato_period = 32768;
}
if( linear_periods ) {
log_2_freq = LOG_2_8363 + ( 4608 - vibrato_period << IBXM.FP_SHIFT ) / 768;
} else {
log_2_freq = module.pal ? LOG_2_8287 : LOG_2_8363;
log_2_freq = log_2_freq + LOG_2_1712 - LogTable.log_2( vibrato_period );
}
log_2_freq += ( key_add << IBXM.FP_SHIFT ) / 12;
step = LogTable.raise_2( log_2_freq - log_2_sampling_rate );
}
}
+111
View File
@@ -0,0 +1,111 @@
package ibxm;
public class Envelope {
public boolean sustain, looped;
private int sustain_tick, loop_start_tick, loop_end_tick;
private int[] ticks, ampls;
public Envelope() {
set_num_points( 1 );
}
public void set_num_points( int num_points ) {
int point;
if( num_points <= 0 ) {
num_points = 1;
}
ticks = new int[ num_points ];
ampls = new int[ num_points ];
set_point( 0, 0, 0, false );
}
/* When you set a point, all subsequent points are reset. */
public void set_point( int point, int tick, int ampl, boolean delta ) {
if( point >= 0 && point < ticks.length ) {
if( point == 0 ) {
tick = 0;
}
if( point > 0 ) {
if( delta ) tick += ticks[ point - 1 ];
if( tick <= ticks[ point - 1 ] ) {
System.out.println( "Envelope: Point not valid (" + tick + " <= " + ticks[ point - 1 ] + ")");
tick = ticks[ point - 1 ] + 1;
}
}
ticks[ point ] = tick;
ampls[ point ] = ampl;
point += 1;
while( point < ticks.length ) {
ticks[ point ] = ticks[ point - 1 ] + 1;
ampls[ point ] = 0;
point += 1;
}
}
}
public void set_sustain_point( int point ) {
if( point < 0 ) {
point = 0;
}
if( point >= ticks.length ) {
point = ticks.length - 1;
}
sustain_tick = ticks[ point ];
}
public void set_loop_points( int start, int end ) {
if( start < 0 ) {
start = 0;
}
if( start >= ticks.length ) {
start = ticks.length - 1;
}
if( end < start || end >= ticks.length ) {
end = start;
}
loop_start_tick = ticks[ start ];
loop_end_tick = ticks[ end ];
}
public int next_tick( int tick, boolean key_on ) {
tick = tick + 1;
if( looped && tick >= loop_end_tick ) {
tick = loop_start_tick;
}
if( sustain && key_on && tick >= sustain_tick ) {
tick = sustain_tick;
}
return tick;
}
public int calculate_ampl( int tick ) {
int idx, point, delta_t, delta_a, ampl;
ampl = ampls[ ticks.length - 1 ];
if( tick < ticks[ ticks.length - 1 ] ) {
point = 0;
for( idx = 1; idx < ticks.length; idx++ ) {
if( ticks[ idx ] <= tick ) {
point = idx;
}
}
delta_t = ticks[ point + 1 ] - ticks[ point ];
delta_a = ampls[ point + 1 ] - ampls[ point ];
ampl = ( delta_a << IBXM.FP_SHIFT ) / delta_t;
ampl = ampl * ( tick - ticks[ point ] ) >> IBXM.FP_SHIFT;
ampl = ampl + ampls[ point ];
}
return ampl;
}
public void dump() {
int idx, tick;
for( idx = 0; idx < ticks.length; idx++ ) {
System.out.println( ticks[ idx ] + ", " + ampls[ idx ] );
}
for( tick = 0; tick < 222; tick++ ) {
System.out.print( calculate_ampl( tick ) + ", " );
}
}
}
+256
View File
@@ -0,0 +1,256 @@
package ibxm;
import java.io.*;
public class FastTracker2 {
public static boolean is_xm( byte[] header_60_bytes ) {
String xm_identifier;
xm_identifier = ascii_text( header_60_bytes, 0, 17 );
return xm_identifier.equals( "Extended Module: " );
}
public static Module load_xm( byte[] header_60_bytes, DataInput data_input ) throws IOException {
int xm_version, song_header_length, sequence_length;
int num_channels, num_patterns, num_instruments, xm_flags, idx;
byte[] structure_header, song_header;
boolean delta_env;
String tracker_name;
Instrument instrument;
Module module;
if( !is_xm( header_60_bytes ) ) {
throw new IllegalArgumentException( "Not an XM file!" );
}
xm_version = unsigned_short_le( header_60_bytes, 58 );
if( xm_version != 0x0104 ) {
throw new IllegalArgumentException( "Sorry, XM version " + xm_version + " is not supported!" );
}
module = new Module();
module.song_title = ascii_text( header_60_bytes, 17, 20 );
tracker_name = ascii_text( header_60_bytes, 38, 20 );
delta_env = tracker_name.startsWith( "DigiBooster Pro" );
structure_header = new byte[ 4 ];
data_input.readFully( structure_header );
song_header_length = int_le( structure_header, 0 );
song_header = new byte[ song_header_length ];
data_input.readFully( song_header, 4, song_header_length - 4 );
sequence_length = unsigned_short_le( song_header, 4 );
module.restart_sequence_index = unsigned_short_le( song_header, 6 );
num_channels = unsigned_short_le( song_header, 8 );
num_patterns = unsigned_short_le( song_header, 10 );
num_instruments = unsigned_short_le( song_header, 12 );
xm_flags = unsigned_short_le( song_header, 14 );
module.linear_periods = ( xm_flags & 0x1 ) == 0x1;
module.global_volume = 64;
module.channel_gain = IBXM.FP_ONE * 3 / 8;
module.default_speed = unsigned_short_le( song_header, 16 );
module.default_tempo = unsigned_short_le( song_header, 18 );
module.set_num_channels( num_channels );
for( idx = 0; idx < num_channels; idx++ ) {
module.set_initial_panning( idx, 128 );
}
module.set_sequence_length( sequence_length );
for( idx = 0; idx < sequence_length; idx++ ) {
module.set_sequence( idx, song_header[ 20 + idx ] & 0xFF );
}
module.set_num_patterns( num_patterns );
for( idx = 0; idx < num_patterns; idx++ ) {
module.set_pattern( idx, read_xm_pattern( data_input, num_channels ) );
}
module.set_num_instruments( num_instruments );
for( idx = 1; idx <= num_instruments; idx++ ) {
try {
instrument = read_xm_instrument( data_input, delta_env );
module.set_instrument( idx, instrument );
} catch( EOFException e ) {
System.out.println( "Instrument " + idx + " is missing!" );
}
}
return module;
}
private static Pattern read_xm_pattern( DataInput data_input, int num_channels ) throws IOException {
int pattern_header_length, packing_type, num_rows, pattern_data_length;
byte[] structure_header, pattern_header, pattern_data;
Pattern pattern;
structure_header = new byte[ 4 ];
data_input.readFully( structure_header );
pattern_header_length = int_le( structure_header, 0 );
pattern_header = new byte[ pattern_header_length ];
data_input.readFully( pattern_header, 4, pattern_header_length - 4 );
packing_type = pattern_header[ 4 ];
if( packing_type != 0 ) {
throw new IllegalArgumentException( "Pattern packing type " + packing_type + " is not supported!" );
}
pattern = new Pattern();
pattern.num_rows = unsigned_short_le( pattern_header, 5 );
pattern_data_length = unsigned_short_le( pattern_header, 7 );
pattern_data = new byte[ pattern_data_length ];
data_input.readFully( pattern_data );
pattern.set_pattern_data( pattern_data );
return pattern;
}
private static Instrument read_xm_instrument( DataInput data_input, boolean delta_env ) throws IOException {
int instrument_header_length, num_samples, idx;
int env_tick, env_ampl, env_num_points, flags;
byte[] structure_header, instrument_header, sample_headers;
Instrument instrument;
Envelope envelope;
structure_header = new byte[ 4 ];
data_input.readFully( structure_header );
instrument_header_length = int_le( structure_header, 0 );
instrument_header = new byte[ instrument_header_length ];
data_input.readFully( instrument_header, 4, instrument_header_length - 4 );
instrument = new Instrument();
instrument.name = ascii_text( instrument_header, 4, 22 );
num_samples = unsigned_short_le( instrument_header, 27 );
if( num_samples > 0 ) {
instrument.set_num_samples( num_samples );
for( idx = 0; idx < 96; idx++ ) {
instrument.set_key_to_sample( idx + 1, instrument_header[ 33 + idx ] & 0xFF );
}
envelope = new Envelope();
env_num_points = instrument_header[ 225 ] & 0xFF;
envelope.set_num_points( env_num_points );
for( idx = 0; idx < env_num_points; idx++ ) {
env_tick = unsigned_short_le( instrument_header, 129 + idx * 4 );
env_ampl = unsigned_short_le( instrument_header, 131 + idx * 4 );
envelope.set_point( idx, env_tick, env_ampl, delta_env );
}
envelope.set_sustain_point( instrument_header[ 227 ] & 0xFF );
envelope.set_loop_points( instrument_header[ 228 ] & 0xFF, instrument_header[ 229 ] & 0xFF );
flags = instrument_header[ 233 ] & 0xFF;
instrument.volume_envelope_active = ( flags & 0x1 ) == 0x1;
envelope.sustain = ( flags & 0x2 ) == 0x2;
envelope.looped = ( flags & 0x4 ) == 0x4;
instrument.set_volume_envelope( envelope );
envelope = new Envelope();
env_num_points = instrument_header[ 226 ] & 0xFF;
envelope.set_num_points( env_num_points );
for( idx = 0; idx < env_num_points; idx++ ) {
env_tick = unsigned_short_le( instrument_header, 177 + idx * 4 );
env_ampl = unsigned_short_le( instrument_header, 179 + idx * 4 );
envelope.set_point( idx, env_tick, env_ampl, delta_env );
}
envelope.set_sustain_point( instrument_header[ 230 ] & 0xFF );
envelope.set_loop_points( instrument_header[ 231 ] & 0xFF, instrument_header[ 232 ] & 0xFF );
flags = instrument_header[ 234 ] & 0xFF;
instrument.panning_envelope_active = ( flags & 0x1 ) == 0x1;
envelope.sustain = ( flags & 0x2 ) == 0x2;
envelope.looped = ( flags & 0x4 ) == 0x4;
instrument.set_panning_envelope( envelope );
instrument.vibrato_type = instrument_header[ 235 ] & 0xFF;
instrument.vibrato_sweep = instrument_header[ 236 ] & 0xFF;
instrument.vibrato_depth = instrument_header[ 237 ] & 0xFF;
instrument.vibrato_rate = instrument_header[ 238 ] & 0xFF;
instrument.volume_fade_out = unsigned_short_le( instrument_header, 239 );
sample_headers = new byte[ num_samples * 40 ];
data_input.readFully( sample_headers );
for( idx = 0; idx < num_samples; idx++ ) {
instrument.set_sample( idx, read_xm_sample( sample_headers, idx, data_input ) );
}
}
return instrument;
}
private static Sample read_xm_sample( byte[] sample_headers, int sample_idx, DataInput data_input ) throws IOException {
int header_offset, sample_length, loop_start, loop_length;
int flags, in_idx, out_idx, sam, last_sam;
int fine_tune, relative_note;
boolean sixteen_bit, ping_pong;
byte[] raw_sample_data;
short[] decoded_sample_data;
Sample sample;
header_offset = sample_idx * 40;
sample = new Sample();
sample_length = int_le( sample_headers, header_offset );
loop_start = int_le( sample_headers, header_offset + 4 );
loop_length = int_le( sample_headers, header_offset + 8 );
sample.volume = sample_headers[ header_offset + 12 ] & 0xFF;
fine_tune = sample_headers[ header_offset + 13 ];
fine_tune = ( fine_tune << IBXM.FP_SHIFT ) / 1536;
sample.set_panning = true;
flags = sample_headers[ header_offset + 14 ] & 0xFF;
if( ( flags & 0x03 ) == 0 ) {
loop_length = 0;
}
ping_pong = ( flags & 0x02 ) == 0x02;
sixteen_bit = ( flags & 0x10 ) == 0x10;
sample.panning = sample_headers[ header_offset + 15 ] & 0xFF;
relative_note = sample_headers[ header_offset + 16 ];
relative_note = ( relative_note << IBXM.FP_SHIFT ) / 12;
sample.transpose = relative_note + fine_tune;
sample.name = ascii_text( sample_headers, header_offset + 18, 22 );
raw_sample_data = new byte[ sample_length ];
try {
data_input.readFully( raw_sample_data );
} catch( EOFException e ) {
System.out.println( "Sample has been truncated!" );
}
in_idx = 0;
out_idx = 0;
sam = 0;
last_sam = 0;
if( sixteen_bit ) {
decoded_sample_data = new short[ sample_length >> 1 ];
while( in_idx < raw_sample_data.length ) {
sam = raw_sample_data[ in_idx ] & 0xFF;
sam = sam | ( ( raw_sample_data[ in_idx + 1 ] & 0xFF ) << 8 );
last_sam = last_sam + sam;
decoded_sample_data[ out_idx ] = ( short ) last_sam;
in_idx += 2;
out_idx += 1;
}
sample.set_sample_data( decoded_sample_data, loop_start >> 1, loop_length >> 1, ping_pong );
} else {
decoded_sample_data = new short[ sample_length ];
while( in_idx < raw_sample_data.length ) {
sam = raw_sample_data[ in_idx ] & 0xFF;
last_sam = last_sam + sam;
decoded_sample_data[ out_idx ] = ( short ) ( last_sam << 8 );
in_idx += 1;
out_idx += 1;
}
sample.set_sample_data( decoded_sample_data, loop_start, loop_length, ping_pong );
}
return sample;
}
private static int unsigned_short_le( byte[] buffer, int offset ) {
int value;
value = buffer[ offset ] & 0xFF;
value = value | ( ( buffer[ offset + 1 ] & 0xFF ) << 8 );
return value;
}
private static int int_le( byte[] buffer, int offset ) {
int value;
value = buffer[ offset ] & 0xFF;
value = value | ( ( buffer[ offset + 1 ] & 0xFF ) << 8 );
value = value | ( ( buffer[ offset + 2 ] & 0xFF ) << 16 );
value = value | ( ( buffer[ offset + 3 ] & 0x7F ) << 24 );
return value;
}
private static String ascii_text( byte[] buffer, int offset, int length ) {
int idx, chr;
byte[] string_buffer;
String string;
string_buffer = new byte[ length ];
for( idx = 0; idx < length; idx++ ) {
chr = buffer[ offset + idx ];
if( chr < 32 ) {
chr = 32;
}
string_buffer[ idx ] = ( byte ) chr;
}
try {
string = new String( string_buffer, 0, length, "ISO-8859-1" );
} catch( UnsupportedEncodingException e ) {
string = "";
}
return string;
}
}
+343
View File
@@ -0,0 +1,343 @@
package ibxm;
public class IBXM {
public static final String VERSION = "ibxm alpha 51 (c)2008 [email protected]";
public static final int FP_SHIFT = 15;
public static final int FP_ONE = 1 << FP_SHIFT;
public static final int FP_MASK = FP_ONE - 1;
private int sampling_rate, resampling_quality, volume_ramp_length;
private int tick_length_samples, current_tick_samples;
private int[] mixing_buffer, volume_ramp_buffer;
private Module module;
private Channel[] channels;
private int[] global_volume, note;
private int current_sequence_index, next_sequence_index;
private int current_row, next_row;
private int tick_counter, ticks_per_row;
private int pattern_loop_count, pattern_loop_channel;
public IBXM( int sample_rate ) {
/** MODIFIED 13 Oct 2009 by Paul Lamb **/
// System.out.println( VERSION );
/***************************************/
if( sample_rate < 8000 ) {
sample_rate = 8000;
}
sampling_rate = sample_rate;
volume_ramp_length = sampling_rate >> 10;
volume_ramp_buffer = new int[ volume_ramp_length * 2 ];
mixing_buffer = new int[ sampling_rate / 6 ];
global_volume = new int[ 1 ];
note = new int[ 5 ];
set_module( new Module() );
set_resampling_quality( 1 );
}
public void set_module( Module m ) {
int channel_idx;
module = m;
channels = new Channel[ module.get_num_channels() ];
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
channels[ channel_idx ] = new Channel( module, sampling_rate, global_volume );
}
set_sequence_index( 0, 0 );
}
public void set_resampling_quality( int quality ) {
resampling_quality = quality;
}
public int calculate_song_duration() {
int song_duration;
set_sequence_index( 0, 0 );
next_tick();
song_duration = tick_length_samples;
while( !next_tick() ) {
song_duration += tick_length_samples;
}
set_sequence_index( 0, 0 );
return song_duration;
}
public void set_sequence_index( int sequence_index, int row ) {
int channel_idx;
global_volume[ 0 ] = 64;
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
channels[ channel_idx ].reset();
channels[ channel_idx ].set_panning( module.get_initial_panning( channel_idx ) );
}
set_global_volume( module.global_volume );
set_speed( 6 );
set_speed( module.default_speed );
set_tempo( 125 );
set_tempo( module.default_tempo );
pattern_loop_count = -1;
next_sequence_index = sequence_index;
next_row = row;
tick_counter = 0;
current_tick_samples = tick_length_samples;
clear_vol_ramp_buffer();
}
public void seek( int sample_position ) {
int idx;
set_sequence_index( 0, 0 );
next_tick();
while( sample_position > tick_length_samples ) {
sample_position -= tick_length_samples;
next_tick();
}
mix_tick();
current_tick_samples = sample_position;
}
public void get_audio( byte[] output_buffer, int frames ) {
int output_idx, mix_idx, mix_end, count, amplitude;
output_idx = 0;
while( frames > 0 ) {
count = tick_length_samples - current_tick_samples;
if( count > frames ) {
count = frames;
}
mix_idx = current_tick_samples << 1;
mix_end = mix_idx + ( count << 1 ) - 1;
while( mix_idx <= mix_end ) {
amplitude = mixing_buffer[ mix_idx ];
if( amplitude > 32767 ) {
amplitude = 32767;
}
if( amplitude < -32768 ) {
amplitude = -32768;
}
output_buffer[ output_idx ] = ( byte ) ( amplitude >> 8 );
output_buffer[ output_idx + 1 ] = ( byte ) ( amplitude & 0xFF );
output_idx += 2;
mix_idx += 1;
}
current_tick_samples = mix_idx >> 1;
frames -= count;
if( frames > 0 ) {
next_tick();
mix_tick();
current_tick_samples = 0;
}
}
}
private void mix_tick() {
int channel_idx, mix_idx, mix_len;
mix_idx = 0;
mix_len = tick_length_samples + volume_ramp_length << 1;
while( mix_idx < mix_len ) {
mixing_buffer[ mix_idx ] = 0;
mix_idx += 1;
}
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
mix_len = tick_length_samples + volume_ramp_length;
channels[ channel_idx ].resample( mixing_buffer, 0, mix_len, resampling_quality );
}
volume_ramp();
}
private boolean next_tick() {
int channel_idx;
boolean song_end;
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
channels[ channel_idx ].update_sample_idx( tick_length_samples );
}
tick_counter -= 1;
if( tick_counter <= 0 ) {
tick_counter = ticks_per_row;
song_end = next_row();
} else {
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
channels[ channel_idx ].tick();
}
song_end = false;
}
return song_end;
}
private boolean next_row() {
int channel_idx, effect, effect_param;
boolean song_end;
Pattern pattern;
song_end = false;
if( next_sequence_index < 0 ) {
/* Bad next sequence index.*/
next_sequence_index = 0;
next_row = 0;
}
if( next_sequence_index >= module.get_sequence_length() ) {
/* End of sequence.*/
song_end = true;
next_sequence_index = module.restart_sequence_index;
if( next_sequence_index < 0 ) {
next_sequence_index = 0;
}
if( next_sequence_index >= module.get_sequence_length() ) {
next_sequence_index = 0;
}
next_row = 0;
}
if( next_sequence_index < current_sequence_index ) {
/* Jump to previous pattern. */
song_end = true;
}
if( next_sequence_index == current_sequence_index ) {
if( next_row <= current_row ) {
if( pattern_loop_count < 0 ) {
/* Jump to previous row in the same pattern, but not a pattern loop. */
song_end = true;
}
}
}
current_sequence_index = next_sequence_index;
pattern = module.get_pattern_from_sequence( current_sequence_index );
if( next_row < 0 || next_row >= pattern.num_rows ) {
/* Bad next row.*/
next_row = 0;
}
current_row = next_row;
next_row = current_row + 1;
if( next_row >= pattern.num_rows ) {
next_sequence_index = current_sequence_index + 1;
next_row = 0;
}
for( channel_idx = 0; channel_idx < channels.length; channel_idx++ ) {
pattern.get_note( note, current_row * channels.length + channel_idx );
effect = note[ 3 ];
effect_param = note[ 4 ];
channels[ channel_idx ].row( note[ 0 ], note[ 1 ], note[ 2 ], effect, effect_param );
switch( effect ) {
case 0x0B:
/* Pattern Jump.*/
if( pattern_loop_count < 0 ) {
next_sequence_index = effect_param;
next_row = 0;
}
break;
case 0x0D:
/* Pattern Break.*/
if( pattern_loop_count < 0 ) {
next_sequence_index = current_sequence_index + 1;
next_row = ( effect_param >> 4 ) * 10 + ( effect_param & 0x0F );
}
break;
case 0x0E:
/* Extended.*/
switch( effect_param & 0xF0 ) {
case 0x60:
/* Pattern loop.*/
if( ( effect_param & 0x0F ) == 0 ) {
/* Set loop marker on this channel. */
channels[ channel_idx ].pattern_loop_row = current_row;
}
if( channels[ channel_idx ].pattern_loop_row < current_row ) {
/* Marker and parameter are valid. Begin looping. */
if( pattern_loop_count < 0 ) {
/* Not already looping, begin. */
pattern_loop_count = effect_param & 0x0F;
pattern_loop_channel = channel_idx;
}
if( pattern_loop_channel == channel_idx ) {
/* Loop in progress on this channel. Next iteration. */
if( pattern_loop_count == 0 ) {
/* Loop finished. */
/* Invalidate current marker. */
channels[ channel_idx ].pattern_loop_row = current_row + 1;
} else {
/* Count must be higher than zero. */
/* Loop and cancel any breaks on this row. */
next_row = channels[ channel_idx ].pattern_loop_row;
next_sequence_index = current_sequence_index;
}
pattern_loop_count -= 1;
}
}
break;
case 0xE0:
/* Pattern delay.*/
tick_counter += ticks_per_row * ( effect_param & 0x0F );
break;
}
break;
case 0x0F:
/* Set Speed/Tempo.*/
if( effect_param < 32 ) {
set_speed( effect_param );
tick_counter = ticks_per_row;
} else {
set_tempo( effect_param );
}
break;
case 0x25:
/* S3M Set Speed.*/
set_speed( effect_param );
tick_counter = ticks_per_row;
break;
}
}
return song_end;
}
private void set_global_volume( int volume ) {
if( volume < 0 ) {
volume = 0;
}
if( volume > 64 ) {
volume = 64;
}
global_volume[ 0 ] = volume;
}
private void set_speed( int speed ) {
if( speed > 0 && speed < 256 ) {
ticks_per_row = speed;
}
}
private void set_tempo( int bpm ) {
if( bpm > 31 && bpm < 256 ) {
tick_length_samples = ( sampling_rate * 5 ) / ( bpm * 2 );
}
}
private void volume_ramp() {
int ramp_idx, next_idx, ramp_end;
int volume_ramp_delta, volume, sample;
sample = 0;
volume_ramp_delta = FP_ONE / volume_ramp_length;
volume = 0;
ramp_idx = 0;
next_idx = 2 * tick_length_samples;
ramp_end = volume_ramp_length * 2 - 1;
while( ramp_idx <= ramp_end ) {
sample = volume_ramp_buffer[ ramp_idx ] * ( FP_ONE - volume ) >> FP_SHIFT;
mixing_buffer[ ramp_idx ] = sample + ( mixing_buffer[ ramp_idx ] * volume >> FP_SHIFT );
volume_ramp_buffer[ ramp_idx ] = mixing_buffer[ next_idx + ramp_idx ];
sample = volume_ramp_buffer[ ramp_idx + 1 ] * ( FP_ONE - volume ) >> FP_SHIFT;
mixing_buffer[ ramp_idx + 1 ] = sample + ( mixing_buffer[ ramp_idx + 1 ] * volume >> FP_SHIFT );
volume_ramp_buffer[ ramp_idx + 1 ] = mixing_buffer[ next_idx + ramp_idx + 1 ];
volume += volume_ramp_delta;
ramp_idx += 2;
}
}
private void clear_vol_ramp_buffer() {
int ramp_idx, ramp_end;
ramp_idx = 0;
ramp_end = volume_ramp_length * 2 - 1;
while( ramp_idx <= ramp_end ) {
volume_ramp_buffer[ ramp_idx ] = 0;
ramp_idx += 1;
}
}
}
+90
View File
@@ -0,0 +1,90 @@
package ibxm;
public class Instrument {
public String name;
public int vibrato_type, vibrato_sweep;
public int vibrato_depth, vibrato_rate;
public boolean volume_envelope_active, panning_envelope_active;
public int volume_fade_out;
private Envelope volume_envelope, panning_envelope;
private int[] key_to_sample;
private Sample[] samples;
public Instrument() {
name = "";
set_volume_envelope( new Envelope() );
set_panning_envelope( new Envelope() );
key_to_sample = new int[ 96 ];
set_num_samples( 1 );
}
public Envelope get_volume_envelope() {
return volume_envelope;
}
public void set_volume_envelope( Envelope envelope ) {
if( envelope != null ) {
volume_envelope = envelope;
}
}
public Envelope get_panning_envelope() {
return panning_envelope;
}
public void set_panning_envelope( Envelope envelope ) {
if( envelope != null ) {
panning_envelope = envelope;
}
}
public Sample get_sample_from_key( int key ) {
int sample_idx;
sample_idx = 0;
if( key > 0 && key <= key_to_sample.length ) {
sample_idx = key_to_sample[ key - 1 ];
}
return get_sample( sample_idx );
}
public void set_key_to_sample( int key, int sample ) {
if( key > 0 && key <= key_to_sample.length ) {
key_to_sample[ key - 1 ] = sample;
}
}
public int get_num_samples() {
return samples.length;
}
public void set_num_samples( int num_samples ) {
if( num_samples < 1 ) {
num_samples = 1;
}
samples = new Sample[ num_samples ];
set_sample( 0, null );
}
public Sample get_sample( int sample_index ) {
Sample sample;
sample = null;
if( sample_index >= 0 && sample_index < samples.length ) {
sample = samples[ sample_index ];
}
if( sample == null ) {
sample = samples[ 0 ];
}
return sample;
}
public void set_sample( int sample_index, Sample sample ) {
if( sample_index >= 0 && sample_index < samples.length ) {
samples[ sample_index ] = sample;
}
if( samples[ 0 ] == null ) {
samples[ 0 ] = new Sample();
}
}
}
+91
View File
@@ -0,0 +1,91 @@
package ibxm;
/*
Base-2 Log and Exp functions, using linear-interpolated tables.
*/
public class LogTable {
private static final int TABLE_SHIFT = 7; // 128 points (+1 for interp)
private static final int INTERP_SHIFT = IBXM.FP_SHIFT - TABLE_SHIFT;
private static final int INTERP_MASK = ( 1 << INTERP_SHIFT ) - 1;
private static final int[] exp_2_table = {
32768, 32945, 33124, 33304, 33485, 33667, 33850, 34033,
34218, 34404, 34591, 34779, 34968, 35157, 35348, 35540,
35733, 35927, 36122, 36319, 36516, 36714, 36913, 37114,
37315, 37518, 37722, 37926, 38132, 38339, 38548, 38757,
38967, 39179, 39392, 39606, 39821, 40037, 40254, 40473,
40693, 40914, 41136, 41359, 41584, 41810, 42037, 42265,
42494, 42725, 42957, 43190, 43425, 43661, 43898, 44136,
44376, 44617, 44859, 45103, 45347, 45594, 45841, 46090,
46340, 46592, 46845, 47099, 47355, 47612, 47871, 48131,
48392, 48655, 48919, 49185, 49452, 49720, 49990, 50262,
50535, 50809, 51085, 51362, 51641, 51922, 52204, 52487,
52772, 53059, 53347, 53636, 53928, 54220, 54515, 54811,
55108, 55408, 55709, 56011, 56315, 56621, 56928, 57238,
57548, 57861, 58175, 58491, 58809, 59128, 59449, 59772,
60096, 60423, 60751, 61081, 61412, 61746, 62081, 62418,
62757, 63098, 63440, 63785, 64131, 64479, 64830, 65182,
65536
};
private static final int[] log_2_table = {
0, 367, 732, 1095, 1454, 1811, 2165, 2517,
2865, 3212, 3556, 3897, 4236, 4572, 4906, 5238,
5568, 5895, 6220, 6542, 6863, 7181, 7497, 7812,
8124, 8434, 8742, 9048, 9352, 9654, 9954, 10252,
10548, 10843, 11136, 11427, 11716, 12003, 12289, 12573,
12855, 13136, 13414, 13692, 13967, 14241, 14514, 14785,
15054, 15322, 15588, 15853, 16117, 16378, 16639, 16898,
17156, 17412, 17667, 17920, 18172, 18423, 18673, 18921,
19168, 19413, 19657, 19900, 20142, 20383, 20622, 20860,
21097, 21333, 21568, 21801, 22034, 22265, 22495, 22724,
22952, 23178, 23404, 23628, 23852, 24074, 24296, 24516,
24736, 24954, 25171, 25388, 25603, 25817, 26031, 26243,
26455, 26665, 26875, 27084, 27292, 27499, 27705, 27910,
28114, 28317, 28520, 28721, 28922, 29122, 29321, 29519,
29716, 29913, 30109, 30304, 30498, 30691, 30884, 31076,
31267, 31457, 31646, 31835, 32023, 32210, 32397, 32582,
32768
};
/*
Calculate log-base-2 of x (non-fixed-point).
A fixed point value is returned.
*/
public static int log_2( int x ) {
int shift;
/* Scale x to range 1.0 <= x < 2.0 */
shift = IBXM.FP_SHIFT;
while( x < IBXM.FP_ONE ) {
x <<= 1;
shift--;
}
while( x >= ( IBXM.FP_ONE << 1 ) ) {
x >>= 1;
shift++;
}
return ( IBXM.FP_ONE * shift ) + eval_table( log_2_table, x - IBXM.FP_ONE );
}
/*
Raise 2 to the power x (fixed point).
A fixed point value is returned.
*/
public static int raise_2( int x ) {
int y;
y = eval_table( exp_2_table, x & IBXM.FP_MASK ) << IBXM.FP_SHIFT;
return y >> IBXM.FP_SHIFT - ( x >> IBXM.FP_SHIFT );
}
private static int eval_table( int[] table, int x ) {
int table_idx, table_frac, c, m, y;
table_idx = x >> INTERP_SHIFT;
table_frac = x & INTERP_MASK;
c = table[ table_idx ];
m = table[ table_idx + 1 ] - c;
y = ( m * table_frac >> INTERP_SHIFT ) + c;
return y >> 15 - IBXM.FP_SHIFT;
}
}
+138
View File
@@ -0,0 +1,138 @@
package ibxm;
public class Module {
public String song_title;
public boolean linear_periods, fast_volume_slides, pal;
public int global_volume, channel_gain;
public int default_speed, default_tempo;
public int restart_sequence_index;
private int[] initial_panning, sequence;
private Pattern[] patterns;
private Instrument[] instruments;
private Pattern default_pattern;
private Instrument default_instrument;
public Module() {
song_title = IBXM.VERSION;
set_num_channels( 1 );
set_sequence_length( 1 );
set_num_patterns( 0 );
set_num_instruments( 0 );
default_pattern = new Pattern();
default_instrument = new Instrument();
}
public int get_num_channels() {
return initial_panning.length;
}
public void set_num_channels( int num_channels ) {
if( num_channels < 1 ) {
num_channels = 1;
}
initial_panning = new int[ num_channels ];
}
public int get_initial_panning( int channel ) {
int panning;
panning = 128;
if( channel >= 0 && channel < initial_panning.length ) {
panning = initial_panning[ channel ];
}
return panning;
}
public void set_initial_panning( int channel, int panning ) {
if( channel >= 0 && channel < initial_panning.length ) {
initial_panning[ channel ] = panning;
}
}
public int get_sequence_length() {
return sequence.length;
}
public void set_sequence_length( int sequence_length ) {
if( sequence_length < 0 ) {
sequence_length = 0;
}
sequence = new int[ sequence_length ];
}
public void set_sequence( int sequence_index, int pattern_index ) {
if( sequence_index >= 0 && sequence_index < sequence.length ) {
sequence[ sequence_index ] = pattern_index;
}
}
public int get_num_patterns() {
return patterns.length;
}
public void set_num_patterns( int num_patterns ) {
if( num_patterns < 0 ) {
num_patterns = 0;
}
patterns = new Pattern[ num_patterns ];
}
public Pattern get_pattern_from_sequence( int sequence_index ) {
Pattern pattern;
pattern = default_pattern;
if( sequence_index >= 0 && sequence_index < sequence.length ) {
pattern = get_pattern( sequence[ sequence_index ] );
}
return pattern;
}
public Pattern get_pattern( int pattern_index ) {
Pattern pattern;
pattern = null;
if( pattern_index >= 0 && pattern_index < patterns.length ) {
pattern = patterns[ pattern_index ];
}
if( pattern == null ) {
pattern = default_pattern;
}
return pattern;
}
public void set_pattern( int pattern_index, Pattern pattern ) {
if( pattern_index >= 0 && pattern_index < patterns.length ) {
patterns[ pattern_index ] = pattern;
}
}
public int get_num_instruments() {
return instruments.length;
}
public void set_num_instruments( int num_instruments ) {
if( num_instruments < 0 ) {
num_instruments = 0;
}
instruments = new Instrument[ num_instruments ];
}
public Instrument get_instrument( int instrument_index ) {
Instrument instrument;
instrument = null;
if( instrument_index > 0 && instrument_index <= instruments.length ) {
instrument = instruments[ instrument_index - 1 ];
}
if( instrument == null ) {
instrument = default_instrument;
}
return instrument;
}
public void set_instrument( int instrument_index, Instrument instrument ) {
if( instrument_index > 0 && instrument_index <= instruments.length ) {
instruments[ instrument_index - 1 ] = instrument;
}
}
}
+61
View File
@@ -0,0 +1,61 @@
package ibxm;
public class Pattern {
public int num_rows;
private int data_offset, note_index;
private byte[] pattern_data;
public Pattern() {
num_rows = 1;
set_pattern_data( new byte[ 0 ] );
}
public void set_pattern_data( byte[] data ) {
if( data != null ) {
pattern_data = data;
}
data_offset = 0;
note_index = 0;
}
public void get_note( int[] note, int index ) {
if( index < note_index ) {
note_index = 0;
data_offset = 0;
}
while( note_index <= index ) {
data_offset = next_note( data_offset, note );
note_index += 1;
}
}
public int next_note( int data_offset, int[] note ) {
int bitmask, field;
if( data_offset < 0 ) {
data_offset = pattern_data.length;
}
bitmask = 0x80;
if( data_offset < pattern_data.length ) {
bitmask = pattern_data[ data_offset ] & 0xFF;
}
if( ( bitmask & 0x80 ) == 0x80 ) {
data_offset += 1;
} else {
bitmask = 0x1F;
}
for( field = 0; field < 5; field++ ) {
note[ field ] = 0;
if( ( bitmask & 0x01 ) == 0x01 ) {
if( data_offset < pattern_data.length ) {
note[ field ] = pattern_data[ data_offset ] & 0xFF;
data_offset += 1;
}
}
bitmask = bitmask >> 1;
}
return data_offset;
}
}
+132
View File
@@ -0,0 +1,132 @@
package ibxm;
import java.io.*;
import javax.sound.sampled.*;
public class Player {
private Thread play_thread;
private IBXM ibxm;
private Module module;
private int song_duration, play_position;
private boolean running, loop;
private byte[] output_buffer;
private SourceDataLine output_line;
/**
Simple command-line test player.
*/
public static void main( String[] args ) throws Exception {
if( args.length < 1 ) {
System.err.println( "Usage: java ibxm.Player <module file>" );
System.exit( 0 );
}
FileInputStream file_input_stream = new FileInputStream( args[ 0 ] );
Player player = new Player();
player.set_module( Player.load_module( file_input_stream ) );
file_input_stream.close();
player.play();
}
/**
Decode the data in the specified InputStream into a Module instance.
@param input an InputStream containing the module file to be decoded.
@throws IllegalArgumentException if the data is not recognised as a module file.
*/
public static Module load_module( InputStream input ) throws IllegalArgumentException, IOException {
DataInputStream data_input_stream = new DataInputStream( input );
/* Check if data is in XM format.*/
byte[] xm_header = new byte[ 60 ];
data_input_stream.readFully( xm_header );
if( FastTracker2.is_xm( xm_header ) )
return FastTracker2.load_xm( xm_header, data_input_stream );
/* Check if data is in ScreamTracker 3 format.*/
byte[] s3m_header = new byte[ 96 ];
System.arraycopy( xm_header, 0, s3m_header, 0, 60 );
data_input_stream.readFully( s3m_header, 60, 36 );
if( ScreamTracker3.is_s3m( s3m_header ) )
return ScreamTracker3.load_s3m( s3m_header, data_input_stream );
/* Check if data is in ProTracker format.*/
byte[] mod_header = new byte[ 1084 ];
System.arraycopy( s3m_header, 0, mod_header, 0, 96 );
data_input_stream.readFully( mod_header, 96, 988 );
return ProTracker.load_mod( mod_header, data_input_stream );
}
/**
Instantiate a new Player.
*/
public Player() throws LineUnavailableException {
ibxm = new IBXM( 48000 );
set_loop( true );
output_line = AudioSystem.getSourceDataLine( new AudioFormat( 48000, 16, 2, true, true ) );
output_buffer = new byte[ 1024 * 4 ];
}
/**
Set the Module instance to be played.
*/
public void set_module( Module m ) {
if( m != null ) module = m;
stop();
ibxm.set_module( module );
song_duration = ibxm.calculate_song_duration();
}
/**
If loop is true, playback will continue indefinitely,
otherwise the module will play through once and stop.
*/
public void set_loop( boolean loop ) {
this.loop = loop;
}
/**
Open the audio device and begin playback.
If a module is already playing it will be restarted.
*/
public void play() {
stop();
play_thread = new Thread( new Driver() );
play_thread.start();
}
/**
Stop playback and close the audio device.
*/
public void stop() {
running = false;
if( play_thread != null ) {
try {
play_thread.join();
} catch( InterruptedException ie ) {}
}
}
private class Driver implements Runnable {
public void run() {
if( running ) return;
try {
output_line.open();
output_line.start();
play_position = 0;
running = true;
while( running ) {
int frames = song_duration - play_position;
if( frames > 1024 ) frames = 1024;
ibxm.get_audio( output_buffer, frames );
output_line.write( output_buffer, 0, frames * 4 );
play_position += frames;
if( play_position >= song_duration ) {
play_position = 0;
if( !loop ) running = false;
}
}
output_line.drain();
output_line.close();
} catch( LineUnavailableException lue ) {
lue.printStackTrace();
}
}
}
}
+234
View File
@@ -0,0 +1,234 @@
package ibxm;
import java.io.*;
public class ProTracker {
public static boolean is_mod( byte[] header_1084_bytes ) {
boolean is_mod;
is_mod = false;
if( calculate_num_channels( header_1084_bytes ) > 0 ) {
is_mod = true;
}
return is_mod;
}
public static Module load_mod( byte[] header_1084_bytes, DataInput data_input ) throws IOException {
int num_channels, channel_idx, panning;
int sequence_length, restart_idx, sequence_idx;
int num_patterns, pattern_idx, instrument_idx;
Module module;
num_channels = calculate_num_channels( header_1084_bytes );
if( num_channels < 1 ) {
throw new IllegalArgumentException( "ProTracker: Unrecognised module format!" );
}
module = new Module();
module.song_title = ascii_text( header_1084_bytes, 0, 20 );
module.pal = ( num_channels == 4 );
module.global_volume = 64;
module.channel_gain = IBXM.FP_ONE * 3 / 8;
module.default_speed = 6;
module.default_tempo = 125;
module.set_num_channels( num_channels );
for( channel_idx = 0; channel_idx < num_channels; channel_idx++ ) {
panning = 64;
if( ( channel_idx & 0x03 ) == 0x01 || ( channel_idx & 0x03 ) == 0x02 ) {
panning = 192;
}
module.set_initial_panning( channel_idx, panning );
}
sequence_length = header_1084_bytes[ 950 ] & 0x7F;
restart_idx = header_1084_bytes[ 951 ] & 0x7F;
if( restart_idx >= sequence_length ) {
restart_idx = 0;
}
module.restart_sequence_index = restart_idx;
module.set_sequence_length( sequence_length );
for( sequence_idx = 0; sequence_idx < sequence_length; sequence_idx++ ) {
module.set_sequence( sequence_idx, header_1084_bytes[ 952 + sequence_idx ] & 0x7F );
}
num_patterns = calculate_num_patterns( header_1084_bytes );
module.set_num_patterns( num_patterns );
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
module.set_pattern( pattern_idx, read_mod_pattern( data_input, num_channels ) );
}
module.set_num_instruments( 31 );
for( instrument_idx = 1; instrument_idx <= 31; instrument_idx++ ) {
module.set_instrument( instrument_idx, read_mod_instrument( header_1084_bytes, instrument_idx, data_input ) );
}
return module;
}
private static int calculate_num_patterns( byte[] module_header ) {
int num_patterns, order_entry, pattern_idx;
num_patterns = 0;
for( pattern_idx = 0; pattern_idx < 128; pattern_idx++ ) {
order_entry = module_header[ 952 + pattern_idx ] & 0x7F;
if( order_entry >= num_patterns ) {
num_patterns = order_entry + 1;
}
}
return num_patterns;
}
private static int calculate_num_channels( byte[] module_header ) {
int num_channels;
switch( ( module_header[ 1082 ] << 8 ) | module_header[ 1083 ] ) {
case 0x4b2e: /* M.K. */
case 0x4b21: /* M!K! */
case 0x542e: /* N.T. */
case 0x5434: /* FLT4 */
num_channels = 4;
break;
case 0x484e: /* xCHN */
num_channels = module_header[ 1080 ] - 48;
break;
case 0x4348: /* xxCH */
num_channels = ( ( module_header[ 1080 ] - 48 ) * 10 ) + ( module_header[ 1081 ] - 48 );
break;
default:
/* Not recognised. */
num_channels = 0;
break;
}
return num_channels;
}
private static Pattern read_mod_pattern( DataInput data_input, int num_channels ) throws IOException {
int input_idx, output_idx;
int period, instrument, effect, effect_param;
Pattern pattern;
byte[] input_pattern_data, output_pattern_data;
pattern = new Pattern();
pattern.num_rows = 64;
input_pattern_data = new byte[ 64 * num_channels * 4 ];
output_pattern_data = new byte[ 64 * num_channels * 5 ];
data_input.readFully( input_pattern_data );
input_idx = 0;
output_idx = 0;
while( input_idx < input_pattern_data.length ) {
period = ( input_pattern_data[ input_idx ] & 0x0F ) << 8;
period = period | ( input_pattern_data[ input_idx + 1 ] & 0xFF );
output_pattern_data[ output_idx ] = to_key( period );
instrument = input_pattern_data[ input_idx ] & 0x10;
instrument = instrument | ( ( input_pattern_data[ input_idx + 2 ] & 0xF0 ) >> 4 );
output_pattern_data[ output_idx + 1 ] = ( byte ) instrument;
effect = input_pattern_data[ input_idx + 2 ] & 0x0F;
effect_param = input_pattern_data[ input_idx + 3 ] & 0xFF;
if( effect == 0x01 && effect_param == 0 ) {
/* Portamento up of zero has no effect. */
effect = 0;
}
if( effect == 0x02 && effect_param == 0 ) {
/* Portamento down of zero has no effect. */
effect = 0;
}
if( effect == 0x08 && num_channels == 4 ) {
/* Some Amiga mods use effect 0x08 for reasons other than panning.*/
effect = 0;
effect_param = 0;
}
if( effect == 0x0A && effect_param == 0 ) {
/* Volume slide of zero has no effect.*/
effect = 0;
}
if( effect == 0x05 && effect_param == 0 ) {
/* Porta + Volume slide of zero has no effect.*/
effect = 0x03;
}
if( effect == 0x06 && effect_param == 0 ) {
/* Vibrato + Volume slide of zero has no effect.*/
effect = 0x04;
}
output_pattern_data[ output_idx + 3 ] = ( byte ) effect;
output_pattern_data[ output_idx + 4 ] = ( byte ) effect_param;
input_idx += 4;
output_idx += 5;
}
pattern.set_pattern_data( output_pattern_data );
return pattern;
}
private static Instrument read_mod_instrument( byte[] mod_header, int idx, DataInput data_input ) throws IOException {
int header_offset, sample_data_length;
int loop_start, loop_length, sample_idx, fine_tune;
Instrument instrument;
Sample sample;
byte[] raw_sample_data;
short[] sample_data;
header_offset = ( idx - 1 ) * 30 + 20;
instrument = new Instrument();
instrument.name = ascii_text( mod_header, header_offset, 22 );
sample = new Sample();
sample_data_length = unsigned_short_be( mod_header, header_offset + 22 ) << 1;
fine_tune = mod_header[ header_offset + 24 ] & 0x0F;
if( fine_tune > 7 ) {
fine_tune -= 16;
}
sample.transpose = ( fine_tune << IBXM.FP_SHIFT ) / 96;
sample.volume = mod_header[ header_offset + 25 ] & 0x7F;
loop_start = unsigned_short_be( mod_header, header_offset + 26 ) << 1;
loop_length = unsigned_short_be( mod_header, header_offset + 28 ) << 1;
if( loop_length < 4 ) {
loop_length = 0;
}
raw_sample_data = new byte[ sample_data_length ];
sample_data = new short[ sample_data_length ];
try {
data_input.readFully( raw_sample_data );
} catch( EOFException e ) {
System.out.println( "ProTracker: Instrument " + idx + " has samples missing." );
}
for( sample_idx = 0; sample_idx < raw_sample_data.length; sample_idx++ ) {
sample_data[ sample_idx ] = ( short ) ( raw_sample_data[ sample_idx ] << 8 );
}
sample.set_sample_data( sample_data, loop_start, loop_length, false );
instrument.set_num_samples( 1 );
instrument.set_sample( 0, sample );
return instrument;
}
private static byte to_key( int period ) {
int oct, key;
if( period < 32 ) {
key = 0;
} else {
oct = LogTable.log_2( 7256 ) - LogTable.log_2( period );
if( oct < 0 ) {
key = 0;
} else {
key = oct * 12;
key = key >> ( IBXM.FP_SHIFT - 1 );
key = ( key >> 1 ) + ( key & 1 );
}
}
return ( byte ) key;
}
private static int unsigned_short_be( byte[] buf, int offset ) {
int value;
value = ( buf[ offset ] & 0xFF ) << 8;
value = value | ( buf[ offset + 1 ] & 0xFF );
return value;
}
private static String ascii_text( byte[] buffer, int offset, int length ) {
int idx, chr;
byte[] string_buffer;
String string;
string_buffer = new byte[ length ];
for( idx = 0; idx < length; idx++ ) {
chr = buffer[ offset + idx ];
if( chr < 32 ) {
chr = 32;
}
string_buffer[ idx ] = ( byte ) chr;
}
try {
string = new String( string_buffer, 0, length, "ISO-8859-1" );
} catch( UnsupportedEncodingException e ) {
string = "";
}
return string;
}
}
+239
View File
@@ -0,0 +1,239 @@
package ibxm;
public class Sample {
public String name;
public boolean set_panning;
public int volume, panning;
public int transpose;
private int loop_start, loop_length;
private short[] sample_data;
/* For the sinc interpolator.*/
private static final int POINT_SHIFT = 4;
private static final int POINTS = 1 << POINT_SHIFT;
private static final int OVERLAP = POINTS >> 1;
private static final int INTERP_SHIFT = IBXM.FP_SHIFT - 4;
private static final int INTERP_BITMASK = ( 1 << INTERP_SHIFT ) - 1;
private static final short[] sinc_table = {
0, -7, 27, -71, 142, -227, 299, 32439, 299, -227, 142, -71, 27, -7, 0, 0,
0, 0, -5, 36, -142, 450, -1439, 32224, 2302, -974, 455, -190, 64, -15, 2, 0,
0, 6, -33, 128, -391, 1042, -2894, 31584, 4540, -1765, 786, -318, 105, -25, 3, 0,
0, 10, -55, 204, -597, 1533, -4056, 30535, 6977, -2573, 1121, -449, 148, -36, 5, 0,
-1, 13, -71, 261, -757, 1916, -4922, 29105, 9568, -3366, 1448, -578, 191, -47, 7, 0,
-1, 15, -81, 300, -870, 2185, -5498, 27328, 12263, -4109, 1749, -698, 232, -58, 9, 0,
-1, 15, -86, 322, -936, 2343, -5800, 25249, 15006, -4765, 2011, -802, 269, -68, 10, 0,
-1, 15, -87, 328, -957, 2394, -5849, 22920, 17738, -5298, 2215, -885, 299, -77, 12, 0,
0, 14, -83, 319, -938, 2347, -5671, 20396, 20396, -5671, 2347, -938, 319, -83, 14, 0,
0, 12, -77, 299, -885, 2215, -5298, 17738, 22920, -5849, 2394, -957, 328, -87, 15, -1,
0, 10, -68, 269, -802, 2011, -4765, 15006, 25249, -5800, 2343, -936, 322, -86, 15, -1,
0, 9, -58, 232, -698, 1749, -4109, 12263, 27328, -5498, 2185, -870, 300, -81, 15, -1,
0, 7, -47, 191, -578, 1448, -3366, 9568, 29105, -4922, 1916, -757, 261, -71, 13, -1,
0, 5, -36, 148, -449, 1121, -2573, 6977, 30535, -4056, 1533, -597, 204, -55, 10, 0,
0, 3, -25, 105, -318, 786, -1765, 4540, 31584, -2894, 1042, -391, 128, -33, 6, 0,
0, 2, -15, 64, -190, 455, -974, 2302, 32224, -1439, 450, -142, 36, -5, 0, 0,
0, 0, -7, 27, -71, 142, -227, 299, 32439, 299, -227, 142, -71, 27, -7, 0
};
public Sample() {
name = "";
set_sample_data( new short[ 0 ], 0, 0, false );
}
public void set_sample_data( short[] data, int loop_start, int loop_length, boolean ping_pong ) {
int offset;
short sample;
if( loop_start < 0 ) {
loop_start = 0;
}
if( loop_start >= data.length ) {
loop_start = data.length - 1;
}
if( loop_start + loop_length > data.length ) {
loop_length = data.length - loop_start;
}
if( loop_length <= 1 ) {
sample_data = new short[ OVERLAP + data.length + OVERLAP * 3 ];
System.arraycopy( data, 0, sample_data, OVERLAP, data.length );
offset = 0;
while( offset < OVERLAP ) {
sample = sample_data[ OVERLAP + data.length - 1 ];
sample = ( short ) ( sample * ( OVERLAP - offset ) / OVERLAP );
sample_data[ OVERLAP + data.length + offset ] = sample;
offset += 1;
}
loop_start = OVERLAP + data.length + OVERLAP;
loop_length = 1;
} else {
if( ping_pong ) {
sample_data = new short[ OVERLAP + loop_start + loop_length * 2 + OVERLAP * 2 ];
System.arraycopy( data, 0, sample_data, OVERLAP, loop_start + loop_length );
offset = 0;
while( offset < loop_length ) {
sample = data[ loop_start + loop_length - offset - 1 ];
sample_data[ OVERLAP + loop_start + loop_length + offset ] = sample;
offset += 1;
}
loop_start = loop_start + OVERLAP;
loop_length = loop_length * 2;
} else {
sample_data = new short[ OVERLAP + loop_start + loop_length + OVERLAP * 2 ];
System.arraycopy( data, 0, sample_data, OVERLAP, loop_start + loop_length );
loop_start = loop_start + OVERLAP;
}
offset = 0;
while( offset < OVERLAP * 2 ) {
sample = sample_data[ loop_start + offset ];
sample_data[ loop_start + loop_length + offset ] = sample;
offset += 1;
}
}
this.loop_start = loop_start;
this.loop_length = loop_length;
}
public void resample_nearest(
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
int[] mix_buffer, int frame_offset, int frames ) {
int loop_end, offset, end, max_sample_idx;
sample_idx += OVERLAP;
loop_end = loop_start + loop_length - 1;
offset = frame_offset << 1;
end = ( frame_offset + frames - 1 ) << 1;
while( frames > 0 ) {
if( sample_idx > loop_end ) {
if( loop_length <= 1 ) {
break;
}
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
}
max_sample_idx = sample_idx + ( ( sample_frac + ( frames - 1 ) * step ) >> IBXM.FP_SHIFT );
if( max_sample_idx > loop_end ) {
while( sample_idx <= loop_end ) {
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * left_gain >> IBXM.FP_SHIFT;
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * right_gain >> IBXM.FP_SHIFT;
sample_frac += step;
sample_idx += sample_frac >> IBXM.FP_SHIFT;
sample_frac &= IBXM.FP_MASK;
}
} else {
while( offset <= end ) {
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * left_gain >> IBXM.FP_SHIFT;
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * right_gain >> IBXM.FP_SHIFT;
sample_frac += step;
sample_idx += sample_frac >> IBXM.FP_SHIFT;
sample_frac &= IBXM.FP_MASK;
}
}
frames = ( end - offset + 2 ) >> 1;
}
}
public void resample_linear(
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
int[] mix_buffer, int frame_offset, int frames ) {
int loop_end, offset, end, max_sample_idx, amplitude;
sample_idx += OVERLAP;
loop_end = loop_start + loop_length - 1;
offset = frame_offset << 1;
end = ( frame_offset + frames - 1 ) << 1;
while( frames > 0 ) {
if( sample_idx > loop_end ) {
if( loop_length <= 1 ) {
break;
}
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
}
max_sample_idx = sample_idx + ( ( sample_frac + ( frames - 1 ) * step ) >> IBXM.FP_SHIFT );
if( max_sample_idx > loop_end ) {
while( sample_idx <= loop_end ) {
amplitude = sample_data[ sample_idx ];
amplitude += ( sample_data[ sample_idx + 1 ] - amplitude ) * sample_frac >> IBXM.FP_SHIFT;
mix_buffer[ offset++ ] += amplitude * left_gain >> IBXM.FP_SHIFT;
mix_buffer[ offset++ ] += amplitude * right_gain >> IBXM.FP_SHIFT;
sample_frac += step;
sample_idx += sample_frac >> IBXM.FP_SHIFT;
sample_frac &= IBXM.FP_MASK;
}
} else {
while( offset <= end ) {
amplitude = sample_data[ sample_idx ];
amplitude += ( sample_data[ sample_idx + 1 ] - amplitude ) * sample_frac >> IBXM.FP_SHIFT;
mix_buffer[ offset++ ] += amplitude * left_gain >> IBXM.FP_SHIFT;
mix_buffer[ offset++ ] += amplitude * right_gain >> IBXM.FP_SHIFT;
sample_frac += step;
sample_idx += sample_frac >> IBXM.FP_SHIFT;
sample_frac &= IBXM.FP_MASK;
}
}
frames = ( end - offset + 2 ) >> 1;
}
}
public void resample_sinc(
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
int[] mix_buffer, int frame_offset, int frames ) {
int offset, end, loop_end, table_idx, a1, a2, amplitude;
loop_end = loop_start + loop_length - 1;
offset = frame_offset << 1;
end = ( frame_offset + frames - 1 ) << 1;
while( offset <= end ) {
if( sample_idx > loop_end ) {
if( loop_length <= 1 ) {
break;
}
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
}
table_idx = ( sample_frac >> INTERP_SHIFT ) << POINT_SHIFT;
a1 = sinc_table[ table_idx + 0 ] * sample_data[ sample_idx + 0 ] >> 15;
a1 += sinc_table[ table_idx + 1 ] * sample_data[ sample_idx + 1 ] >> 15;
a1 += sinc_table[ table_idx + 2 ] * sample_data[ sample_idx + 2 ] >> 15;
a1 += sinc_table[ table_idx + 3 ] * sample_data[ sample_idx + 3 ] >> 15;
a1 += sinc_table[ table_idx + 4 ] * sample_data[ sample_idx + 4 ] >> 15;
a1 += sinc_table[ table_idx + 5 ] * sample_data[ sample_idx + 5 ] >> 15;
a1 += sinc_table[ table_idx + 6 ] * sample_data[ sample_idx + 6 ] >> 15;
a1 += sinc_table[ table_idx + 7 ] * sample_data[ sample_idx + 7 ] >> 15;
a1 += sinc_table[ table_idx + 8 ] * sample_data[ sample_idx + 8 ] >> 15;
a1 += sinc_table[ table_idx + 9 ] * sample_data[ sample_idx + 9 ] >> 15;
a1 += sinc_table[ table_idx + 10 ] * sample_data[ sample_idx + 10 ] >> 15;
a1 += sinc_table[ table_idx + 11 ] * sample_data[ sample_idx + 11 ] >> 15;
a1 += sinc_table[ table_idx + 12 ] * sample_data[ sample_idx + 12 ] >> 15;
a1 += sinc_table[ table_idx + 13 ] * sample_data[ sample_idx + 13 ] >> 15;
a1 += sinc_table[ table_idx + 14 ] * sample_data[ sample_idx + 14 ] >> 15;
a1 += sinc_table[ table_idx + 15 ] * sample_data[ sample_idx + 15 ] >> 15;
a2 = sinc_table[ table_idx + 16 ] * sample_data[ sample_idx + 0 ] >> 15;
a2 += sinc_table[ table_idx + 17 ] * sample_data[ sample_idx + 1 ] >> 15;
a2 += sinc_table[ table_idx + 18 ] * sample_data[ sample_idx + 2 ] >> 15;
a2 += sinc_table[ table_idx + 19 ] * sample_data[ sample_idx + 3 ] >> 15;
a2 += sinc_table[ table_idx + 20 ] * sample_data[ sample_idx + 4 ] >> 15;
a2 += sinc_table[ table_idx + 21 ] * sample_data[ sample_idx + 5 ] >> 15;
a2 += sinc_table[ table_idx + 22 ] * sample_data[ sample_idx + 6 ] >> 15;
a2 += sinc_table[ table_idx + 23 ] * sample_data[ sample_idx + 7 ] >> 15;
a2 += sinc_table[ table_idx + 24 ] * sample_data[ sample_idx + 8 ] >> 15;
a2 += sinc_table[ table_idx + 25 ] * sample_data[ sample_idx + 9 ] >> 15;
a2 += sinc_table[ table_idx + 26 ] * sample_data[ sample_idx + 10 ] >> 15;
a2 += sinc_table[ table_idx + 27 ] * sample_data[ sample_idx + 11 ] >> 15;
a2 += sinc_table[ table_idx + 28 ] * sample_data[ sample_idx + 12 ] >> 15;
a2 += sinc_table[ table_idx + 29 ] * sample_data[ sample_idx + 13 ] >> 15;
a2 += sinc_table[ table_idx + 30 ] * sample_data[ sample_idx + 14 ] >> 15;
a2 += sinc_table[ table_idx + 31 ] * sample_data[ sample_idx + 15 ] >> 15;
amplitude = a1 + ( ( a2 - a1 ) * ( sample_frac & INTERP_BITMASK ) >> INTERP_SHIFT );
mix_buffer[ offset ] += amplitude * left_gain >> IBXM.FP_SHIFT;
mix_buffer[ offset + 1 ] += amplitude * right_gain >> IBXM.FP_SHIFT;
offset += 2;
sample_frac += step;
sample_idx += sample_frac >> IBXM.FP_SHIFT;
sample_frac &= IBXM.FP_MASK;
}
}
public boolean has_finished( int sample_idx ) {
boolean finished;
finished = false;
if( loop_length <= 1 && sample_idx > loop_start ) {
finished = true;
}
return finished;
}
}
+489
View File
@@ -0,0 +1,489 @@
package ibxm;
import java.io.*;
public class ScreamTracker3 {
private static final int[] effect_map = new int[] {
0xFF,
0x25, /* A: Set Speed.*/
0x0B, /* B: Pattern Jump.*/
0x0D, /* C: Pattern Break.*/
0x0A, /* D: Volume Slide.*/
0x02, /* E: Portamento Down.*/
0x01, /* F: Portamento Up.*/
0x03, /* G: Tone Portamento.*/
0x04, /* H: Vibrato.*/
0x1D, /* I: Tremor.*/
0x00, /* J: Arpeggio.*/
0x06, /* K: Vibrato + Volume Slide.*/
0x05, /* L: Tone Portamento + Volume Slide.*/
0xFF, /* M: */
0xFF, /* N: */
0x09, /* O: Sample Offset.*/
0xFF, /* P: */
0x1B, /* Q: Retrig + Volume Slide.*/
0x07, /* R: Tremolo.*/
0x0E, /* S: Extended Effects.*/
0x0F, /* T: Set Tempo.*/
0x24, /* U: Fine Vibrato.*/
0x10, /* V: Set Global Volume. */
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF
};
private static final int[] effect_s_map = new int[] {
0x00, /* 0: Set Filter.*/
0x03, /* 1: Glissando.*/
0x05, /* 2: Set Fine Tune.*/
0x04, /* 3: Set Vibrato Waveform.*/
0x07, /* 4: Set Tremolo Waveform.*/
0xFF, /* 5: */
0xFF, /* 6: */
0xFF, /* 7: */
0x08, /* 8: Set Panning.*/
0xFF, /* 9: */
0x09, /* A: Stereo Control.*/
0x06, /* B: Pattern Loop.*/
0x0C, /* C: Note Cut.*/
0x0D, /* D: Note Delay.*/
0x0E, /* E: Pattern Delay.*/
0x0F /* F: Invert Loop.*/
};
public static boolean is_s3m( byte[] header_96_bytes ) {
String s3m_identifier;
s3m_identifier = ascii_text( header_96_bytes, 44, 4 );
return s3m_identifier.equals( "SCRM" );
}
public static Module load_s3m( byte[] header_96_bytes, DataInput data_input ) throws IOException {
int num_pattern_orders, num_instruments, num_patterns, num_channels;
int flags, tracker_version, master_volume, panning, channel_config, sequence_length;
int instrument_idx, pattern_idx, channel_idx, order_idx, panning_offset;
boolean signed_samples, stereo_mode, default_panning;
int[] channel_map, sequence;
byte[] s3m_file;
Module module;
Instrument instrument;
s3m_file = read_s3m_file( header_96_bytes, data_input );
module = new Module();
module.song_title = ascii_text( s3m_file, 0, 28 );
num_pattern_orders = get_num_pattern_orders( s3m_file );
num_instruments = get_num_instruments( s3m_file );
num_patterns = get_num_patterns( s3m_file );
flags = unsigned_short_le( s3m_file, 38 );
tracker_version = unsigned_short_le( s3m_file, 40 );
if( ( flags & 0x40 ) == 0x40 || tracker_version == 0x1300 ) {
module.fast_volume_slides = true;
}
signed_samples = false;
if( unsigned_short_le( s3m_file, 42 ) == 0x01 ) {
signed_samples = true;
}
module.global_volume = s3m_file[ 48 ] & 0xFF;
module.default_speed = s3m_file[ 49 ] & 0xFF;
module.default_tempo = s3m_file[ 50 ] & 0xFF;
master_volume = s3m_file[ 51 ] & 0x7F;
module.channel_gain = ( master_volume << IBXM.FP_SHIFT ) >> 7;
stereo_mode = ( s3m_file[ 51 ] & 0x80 ) == 0x80;
default_panning = ( s3m_file[ 53 ] & 0xFF ) == 0xFC;
channel_map = new int[ 32 ];
num_channels = 0;
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
channel_config = s3m_file[ 64 + channel_idx ] & 0xFF;
channel_map[ channel_idx ] = -1;
if( channel_config < 16 ) {
channel_map[ channel_idx ] = num_channels;
num_channels += 1;
}
}
module.set_num_channels( num_channels );
panning_offset = 96 + num_pattern_orders + num_instruments * 2 + num_patterns * 2;
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
if( channel_map[ channel_idx ] < 0 ) continue;
panning = 7;
if( stereo_mode ) {
panning = 12;
if( ( s3m_file[ 64 + channel_idx ] & 0xFF ) < 8 ) {
panning = 3;
}
}
if( default_panning ) {
flags = s3m_file[ panning_offset + channel_idx ] & 0xFF;
if( ( flags & 0x20 ) == 0x20 ) {
panning = flags & 0xF;
}
}
module.set_initial_panning( channel_map[ channel_idx ], panning * 17 );
}
sequence = read_s3m_sequence( s3m_file );
module.set_sequence_length( sequence.length );
for( order_idx = 0; order_idx < sequence.length; order_idx++ ) {
module.set_sequence( order_idx, sequence[ order_idx ] );
}
module.set_num_instruments( num_instruments );
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
instrument = read_s3m_instrument( s3m_file, instrument_idx, signed_samples );
module.set_instrument( instrument_idx + 1, instrument );
}
module.set_num_patterns( num_patterns );
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
module.set_pattern( pattern_idx, read_s3m_pattern( s3m_file, pattern_idx, channel_map ) );
}
return module;
}
private static int[] read_s3m_sequence( byte[] s3m_file ) {
int num_pattern_orders, sequence_length;
int sequence_idx, order_idx, pattern_order;
int[] sequence;
num_pattern_orders = get_num_pattern_orders( s3m_file );
sequence_length = 0;
for( order_idx = 0; order_idx < num_pattern_orders; order_idx++ ) {
pattern_order = s3m_file[ 96 + order_idx ] & 0xFF;
if( pattern_order == 255 ) {
break;
} else if( pattern_order < 254 ) {
sequence_length += 1;
}
}
sequence = new int[ sequence_length ];
sequence_idx = 0;
for( order_idx = 0; order_idx < num_pattern_orders; order_idx++ ) {
pattern_order = s3m_file[ 96 + order_idx ] & 0xFF;
if( pattern_order == 255 ) {
break;
} else if( pattern_order < 254 ) {
sequence[ sequence_idx ] = pattern_order;
sequence_idx += 1;
}
}
return sequence;
}
private static Instrument read_s3m_instrument( byte[] s3m_file, int instrument_idx, boolean signed_samples ) {
int instrument_offset;
int sample_data_offset, sample_data_length;
int loop_start, loop_length, c2_rate, sample_idx, amplitude;
boolean sixteen_bit;
Instrument instrument;
Sample sample;
short[] sample_data;
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
instrument = new Instrument();
instrument.name = ascii_text( s3m_file, instrument_offset + 48, 28 );
sample = new Sample();
if( s3m_file[ instrument_offset ] == 1 ) {
sample_data_length = get_sample_data_length( s3m_file, instrument_offset );
loop_start = unsigned_short_le( s3m_file, instrument_offset + 20 );
loop_length = unsigned_short_le( s3m_file, instrument_offset + 24 ) - loop_start;
sample.volume = s3m_file[ instrument_offset + 28 ] & 0xFF;
if( s3m_file[ instrument_offset + 30 ] != 0 ) {
throw new IllegalArgumentException( "ScreamTracker3: Packed samples not supported!" );
}
if( ( s3m_file[ instrument_offset + 31 ] & 0x01 ) == 0 ) {
loop_length = 0;
}
if( ( s3m_file[ instrument_offset + 31 ] & 0x02 ) != 0 ) {
throw new IllegalArgumentException( "ScreamTracker3: Stereo samples not supported!" );
}
sixteen_bit = ( s3m_file[ instrument_offset + 31 ] & 0x04 ) != 0;
c2_rate = unsigned_short_le( s3m_file, instrument_offset + 32 );
sample.transpose = LogTable.log_2( c2_rate ) - LogTable.log_2( 8363 );
sample_data_offset = get_sample_data_offset( s3m_file, instrument_offset );
if( sixteen_bit ) {
if( signed_samples ) {
throw new IllegalArgumentException( "ScreamTracker3: Signed 16-bit samples not supported!" );
}
sample_data_length >>= 1;
sample_data = new short[ sample_data_length ];
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
amplitude = s3m_file[ sample_data_offset + sample_idx * 2 ] & 0xFF;
amplitude |= ( s3m_file[ sample_data_offset + sample_idx * 2 + 1 ] & 0xFF ) << 8;
sample_data[ sample_idx ] = ( short ) ( amplitude - 32768 );
}
} else {
sample_data = new short[ sample_data_length ];
if( signed_samples ) {
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
amplitude = s3m_file[ sample_data_offset + sample_idx ] << 8;
sample_data[ sample_idx ] = ( short ) amplitude;
}
} else {
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
amplitude = ( s3m_file[ sample_data_offset + sample_idx ] & 0xFF ) << 8;
sample_data[ sample_idx ] = ( short ) ( amplitude - 32768 );
}
}
}
sample.set_sample_data( sample_data, loop_start, loop_length, false );
}
instrument.set_num_samples( 1 );
instrument.set_sample( 0, sample );
return instrument;
}
private static Pattern read_s3m_pattern( byte[] s3m_file, int pattern_idx, int[] channel_map ) {
int pattern_offset;
int num_channels, num_notes;
int row_idx, channel_idx, note_idx;
int token, key, volume_column, effect, effect_param;
byte[] pattern_data;
Pattern pattern;
num_channels = 0;
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
if( channel_map[ channel_idx ] >= num_channels ) {
num_channels = channel_idx + 1;
}
}
num_notes = num_channels * 64;
pattern_data = new byte[ num_notes * 5 ];
row_idx = 0;
pattern_offset = get_pattern_offset( s3m_file, pattern_idx ) + 2;
while( row_idx < 64 ) {
token = s3m_file[ pattern_offset ] & 0xFF;
pattern_offset += 1;
if( token > 0 ) {
channel_idx = channel_map[ token & 0x1F ];
note_idx = ( num_channels * row_idx + channel_idx ) * 5;
if( ( token & 0x20 ) == 0x20 ) {
/* Key + Instrument.*/
if( channel_idx >= 0 ) {
key = s3m_file[ pattern_offset ] & 0xFF;
if( key == 255 ) {
key = 0;
} else if( key == 254 ) {
key = 97;
} else {
key = ( ( key & 0xF0 ) >> 4 ) * 12 + ( key & 0x0F ) + 1;
while( key > 96 ) {
key = key - 12;
}
}
pattern_data[ note_idx ] = ( byte ) key;
pattern_data[ note_idx + 1 ] = s3m_file[ pattern_offset + 1 ];
}
pattern_offset += 2;
}
if( ( token & 0x40 ) == 0x40 ) {
/* Volume.*/
if( channel_idx >= 0 ) {
volume_column = ( s3m_file[ pattern_offset ] & 0xFF ) + 0x10;
pattern_data[ note_idx + 2 ] = ( byte ) volume_column;
}
pattern_offset += 1;
}
if( ( token & 0x80 ) == 0x80 ) {
/* Effect + Param.*/
if( channel_idx >= 0 ) {
effect = s3m_file[ pattern_offset ] & 0xFF;
effect_param = s3m_file[ pattern_offset + 1 ] & 0xFF;
effect = effect_map[ effect & 0x1F ];
if( effect == 0xFF ) {
effect = 0;
effect_param = 0;
}
if( effect == 0x0E ) {
effect = effect_s_map[ ( effect_param & 0xF0 ) >> 4 ];
effect_param = effect_param & 0x0F;
switch( effect ) {
case 0x08:
effect = 0x08;
effect_param = effect_param * 17;
break;
case 0x09:
effect = 0x08;
if( effect_param > 7 ) {
effect_param -= 8;
} else {
effect_param += 8;
}
effect_param = effect_param * 17;
break;
case 0xFF:
effect = 0;
effect_param = 0;
break;
default:
effect_param = ( ( effect & 0x0F ) << 4 ) | ( effect_param & 0x0F );
effect = 0x0E;
break;
}
}
pattern_data[ note_idx + 3 ] = ( byte ) effect;
pattern_data[ note_idx + 4 ] = ( byte ) effect_param;
}
pattern_offset += 2;
}
} else {
row_idx += 1;
}
}
pattern = new Pattern();
pattern.num_rows = 64;
pattern.set_pattern_data( pattern_data );
return pattern;
}
private static byte[] read_s3m_file( byte[] header_96_bytes, DataInput data_input ) throws IOException {
int s3m_file_length;
int num_pattern_orders, num_instruments, num_patterns;
int instrument_idx, pattern_idx;
int instrument_offset, sample_data_offset, pattern_offset;
byte[] s3m_file;
if( !is_s3m( header_96_bytes ) ) {
throw new IllegalArgumentException( "ScreamTracker3: Not an S3M file!" );
}
s3m_file = header_96_bytes;
s3m_file_length = header_96_bytes.length;
num_pattern_orders = get_num_pattern_orders( s3m_file );
num_instruments = get_num_instruments( s3m_file );
num_patterns = get_num_patterns( s3m_file );
s3m_file_length += num_pattern_orders;
s3m_file_length += num_instruments * 2;
s3m_file_length += num_patterns * 2;
/* Read enough of file to calculate the length.*/
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
instrument_offset += 80;
if( instrument_offset > s3m_file_length ) {
s3m_file_length = instrument_offset;
}
}
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
pattern_offset = get_pattern_offset( s3m_file, pattern_idx );
pattern_offset += 2;
if( pattern_offset > s3m_file_length ) {
s3m_file_length = pattern_offset;
}
}
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
/* Read rest of file.*/
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
sample_data_offset = get_sample_data_offset( s3m_file, instrument_offset );
sample_data_offset += get_sample_data_length( s3m_file, instrument_offset );
if( sample_data_offset > s3m_file_length ) {
s3m_file_length = sample_data_offset;
}
}
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
pattern_offset = get_pattern_offset( s3m_file, pattern_idx );
pattern_offset += get_pattern_length( s3m_file, pattern_offset );
pattern_offset += 2;
if( pattern_offset > s3m_file_length ) {
s3m_file_length = pattern_offset;
}
}
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
return s3m_file;
}
private static int get_num_pattern_orders( byte[] s3m_file ) {
int num_pattern_orders;
num_pattern_orders = unsigned_short_le( s3m_file, 32 );
return num_pattern_orders;
}
private static int get_num_instruments( byte[] s3m_file ) {
int num_instruments;
num_instruments = unsigned_short_le( s3m_file, 34 );
return num_instruments;
}
private static int get_num_patterns( byte[] s3m_file ) {
int num_patterns;
num_patterns = unsigned_short_le( s3m_file, 36 );
return num_patterns;
}
private static int get_instrument_offset( byte[] s3m_file, int instrument_idx ) {
int instrument_offset, pointer_offset;
pointer_offset = 96 + get_num_pattern_orders( s3m_file );
instrument_offset = unsigned_short_le( s3m_file, pointer_offset + instrument_idx * 2 ) << 4;
return instrument_offset;
}
private static int get_sample_data_offset( byte[] s3m_file, int instrument_offset ) {
int sample_data_offset;
sample_data_offset = 0;
if( s3m_file[ instrument_offset ] == 1 ) {
sample_data_offset = ( s3m_file[ instrument_offset + 13 ] & 0xFF ) << 20;
sample_data_offset |= unsigned_short_le( s3m_file, instrument_offset + 14 ) << 4;
}
return sample_data_offset;
}
private static int get_sample_data_length( byte[] s3m_file, int instrument_offset ) {
int sample_data_length;
boolean sixteen_bit;
sample_data_length = 0;
if( s3m_file[ instrument_offset ] == 1 ) {
sample_data_length = unsigned_short_le( s3m_file, instrument_offset + 16 );
sixteen_bit = ( s3m_file[ instrument_offset + 31 ] & 0x04 ) != 0;
if( sixteen_bit ) {
sample_data_length <<= 1;
}
}
return sample_data_length;
}
private static int get_pattern_offset( byte[] s3m_file, int pattern_idx ) {
int pattern_offset, pointer_offset;
pointer_offset = 96 + get_num_pattern_orders( s3m_file );
pointer_offset += get_num_instruments( s3m_file ) * 2;
pattern_offset = unsigned_short_le( s3m_file, pointer_offset + pattern_idx * 2 ) << 4;
return pattern_offset;
}
private static int get_pattern_length( byte[] s3m_file, int pattern_offset ) {
int pattern_length;
pattern_length = unsigned_short_le( s3m_file, pattern_offset );
return pattern_length;
}
private static byte[] read_more( byte[] old_data, int new_length, DataInput data_input ) throws IOException {
byte[] new_data;
new_data = old_data;
if( new_length > old_data.length ) {
new_data = new byte[ new_length ];
System.arraycopy( old_data, 0, new_data, 0, old_data.length );
try {
data_input.readFully( new_data, old_data.length, new_data.length - old_data.length );
} catch( EOFException e ) {
System.out.println( "ScreamTracker3: Module has been truncated!" );
}
}
return new_data;
}
private static int unsigned_short_le( byte[] buffer, int offset ) {
int value;
value = buffer[ offset ] & 0xFF;
value = value | ( ( buffer[ offset + 1 ] & 0xFF ) << 8 );
return value;
}
private static String ascii_text( byte[] buffer, int offset, int length ) {
int idx, chr;
byte[] string_buffer;
String string;
string_buffer = new byte[ length ];
for( idx = 0; idx < length; idx++ ) {
chr = buffer[ offset + idx ];
if( chr < 32 ) {
chr = 32;
}
string_buffer[ idx ] = ( byte ) chr;
}
try {
string = new String( string_buffer, 0, length, "ISO-8859-1" );
} catch( UnsupportedEncodingException e ) {
string = "";
}
return string;
}
}
@@ -0,0 +1,141 @@
package net.minecraftforge.client;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiChat;
import net.minecraft.command.CommandException;
import net.minecraft.command.CommandHandler;
import net.minecraft.command.ICommand;
import net.minecraft.command.ICommandSender;
import net.minecraft.command.WrongUsageException;
import net.minecraft.util.ChatMessageComponent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.CommandEvent;
import cpw.mods.fml.client.FMLClientHandler;
import static net.minecraft.util.EnumChatFormatting.*;
/**
* The class that handles client-side chat commands. You should register any
* commands that you want handled on the client with this command handler.
*
* If there is a command with the same name registered both on the server and
* client, the client takes precedence!
*
*/
public class ClientCommandHandler extends CommandHandler
{
public static final ClientCommandHandler instance = new ClientCommandHandler();
public String[] latestAutoComplete = null;
/**
* @return 1 if successfully executed, 0 if wrong usage, it doesn't exist or
* it was canceled.
*/
@Override
public int executeCommand(ICommandSender sender, String message)
{
message = message.trim();
if (message.startsWith("/"))
{
message = message.substring(1);
}
String[] temp = message.split(" ");
String[] args = new String[temp.length - 1];
String commandName = temp[0];
System.arraycopy(temp, 1, args, 0, args.length);
ICommand icommand = (ICommand) getCommands().get(commandName);
try
{
if (icommand == null)
{
return 0;
}
if (icommand.canCommandSenderUseCommand(sender))
{
CommandEvent event = new CommandEvent(icommand, sender, args);
if (MinecraftForge.EVENT_BUS.post(event))
{
if (event.exception != null)
{
throw event.exception;
}
return 0;
}
icommand.processCommand(sender, args);
return 1;
}
else
{
sender.sendChatToPlayer(format("commands.generic.permission").setColor(RED));
}
}
catch (WrongUsageException wue)
{
sender.sendChatToPlayer(format("commands.generic.usage", format(wue.getMessage(), wue.getErrorOjbects())).setColor(RED));
}
catch (CommandException ce)
{
sender.sendChatToPlayer(format(ce.getMessage(), ce.getErrorOjbects()).setColor(RED));
}
catch (Throwable t)
{
sender.sendChatToPlayer(format("commands.generic.exception").setColor(RED));
t.printStackTrace();
}
return 0;
}
//Couple of helpers because the mcp names are stupid and long...
private ChatMessageComponent format(String str, Object... args)
{
return ChatMessageComponent.createFromTranslationWithSubstitutions(str, args);
}
private ChatMessageComponent format(String str)
{
return ChatMessageComponent.createFromTranslationKey(str);
}
public void autoComplete(String leftOfCursor, String full)
{
latestAutoComplete = null;
if (leftOfCursor.charAt(0) == '/')
{
leftOfCursor = leftOfCursor.substring(1);
Minecraft mc = FMLClientHandler.instance().getClient();
if (mc.currentScreen instanceof GuiChat)
{
List<String> commands = getPossibleCommands(mc.thePlayer, leftOfCursor);
if (commands != null && !commands.isEmpty())
{
if (leftOfCursor.indexOf(' ') == -1)
{
for (int i = 0; i < commands.size(); i++)
{
commands.set(i, GRAY + "/" + commands.get(i) + RESET);
}
}
else
{
for (int i = 0; i < commands.size(); i++)
{
commands.set(i, GRAY + commands.get(i) + RESET);
}
}
latestAutoComplete = commands.toArray(new String[commands.size()]);
}
}
}
}
}
@@ -0,0 +1,44 @@
package net.minecraftforge.client;
import net.minecraft.world.EnumGameType;
import net.minecraft.util.EnumOS;
import net.minecraft.client.settings.EnumOptions;
import net.minecraft.item.EnumRarity;
import net.minecraftforge.common.EnumHelper;
public class EnumHelperClient extends EnumHelper
{
private static Class[][] clentTypes =
{
{EnumGameType.class, int.class, String.class},
{EnumOptions.class, String.class, boolean.class, boolean.class},
{EnumOS.class},
{EnumRarity.class, int.class, String.class}
};
public static EnumGameType addGameType(String name, int id, String displayName)
{
return addEnum(EnumGameType.class, name, id, displayName);
}
public static EnumOptions addOptions(String name, String langName, boolean isSlider, boolean isToggle)
{
return addEnum(EnumOptions.class, name, langName, isSlider, isToggle);
}
public static EnumOS addOS2(String name)
{
return addEnum(EnumOS.class, name);
}
public static EnumRarity addRarity(String name, int color, String displayName)
{
return addEnum(EnumRarity.class, name, color, displayName);
}
public static <T extends Enum<? >> T addEnum(Class<T> enumType, String enumName, Object... paramValues)
{
return addEnum(clentTypes, enumType, enumName, paramValues);
}
}
@@ -0,0 +1,393 @@
package net.minecraftforge.client;
import java.util.HashMap;
import java.util.Random;
import java.util.TreeSet;
import javax.imageio.ImageIO;
import net.minecraftforge.client.event.MouseEvent;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraftforge.client.event.FOVUpdateEvent;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.PixelFormat;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.client.registry.RenderingRegistry;
import net.minecraft.client.Minecraft;
import net.minecraft.block.Block;
import net.minecraft.block.BlockFluid;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.RenderItem;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraftforge.client.IItemRenderer.ItemRenderType;
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.common.ForgeDummyContainer;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.RenderBlockFluid;
import static net.minecraftforge.client.IItemRenderer.ItemRenderType.*;
import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.*;
public class ForgeHooksClient
{
private static final ResourceLocation ITEM_GLINT = new ResourceLocation("textures/misc/enchanted_item_glint.png");
static TextureManager engine()
{
return FMLClientHandler.instance().getClient().renderEngine;
}
@Deprecated
public static String getArmorTexture(Entity entity, ItemStack armor, String _default, int slot, int layer, String type)
{
return getArmorTexture(entity, armor, _default, slot, type);
}
public static String getArmorTexture(Entity entity, ItemStack armor, String _default, int slot, String type)
{
String result = armor.getItem().getArmorTexture(armor, entity, slot, type);
return result != null ? result : _default;
}
public static boolean renderEntityItem(EntityItem entity, ItemStack item, float bobing, float rotation, Random random, TextureManager engine, RenderBlocks renderBlocks)
{
IItemRenderer customRenderer = MinecraftForgeClient.getItemRenderer(item, ENTITY);
if (customRenderer == null)
{
return false;
}
if (customRenderer.shouldUseRenderHelper(ENTITY, item, ENTITY_ROTATION))
{
GL11.glRotatef(rotation, 0.0F, 1.0F, 0.0F);
}
if (!customRenderer.shouldUseRenderHelper(ENTITY, item, ENTITY_BOBBING))
{
GL11.glTranslatef(0.0F, -bobing, 0.0F);
}
boolean is3D = customRenderer.shouldUseRenderHelper(ENTITY, item, BLOCK_3D);
engine.bindTexture(item.getItemSpriteNumber() == 0 ? TextureMap.locationBlocksTexture : TextureMap.locationItemsTexture);
Block block = (item.itemID < Block.blocksList.length ? Block.blocksList[item.itemID] : null);
if (is3D || (block != null && RenderBlocks.renderItemIn3d(block.getRenderType())))
{
int renderType = (block != null ? block.getRenderType() : 1);
float scale = (renderType == 1 || renderType == 19 || renderType == 12 || renderType == 2 ? 0.5F : 0.25F);
if (RenderItem.renderInFrame)
{
GL11.glScalef(1.25F, 1.25F, 1.25F);
GL11.glTranslatef(0.0F, 0.05F, 0.0F);
GL11.glRotatef(-90.0F, 0.0F, 1.0F, 0.0F);
}
GL11.glScalef(scale, scale, scale);
int size = item.stackSize;
int count = (size > 40 ? 5 : (size > 20 ? 4 : (size > 5 ? 3 : (size > 1 ? 2 : 1))));
for(int j = 0; j < count; j++)
{
GL11.glPushMatrix();
if (j > 0)
{
GL11.glTranslatef(
((random.nextFloat() * 2.0F - 1.0F) * 0.2F) / scale,
((random.nextFloat() * 2.0F - 1.0F) * 0.2F) / scale,
((random.nextFloat() * 2.0F - 1.0F) * 0.2F) / scale);
}
customRenderer.renderItem(ENTITY, item, renderBlocks, entity);
GL11.glPopMatrix();
}
}
else
{
GL11.glScalef(0.5F, 0.5F, 0.5F);
customRenderer.renderItem(ENTITY, item, renderBlocks, entity);
}
return true;
}
public static boolean renderInventoryItem(RenderBlocks renderBlocks, TextureManager engine, ItemStack item, boolean inColor, float zLevel, float x, float y)
{
IItemRenderer customRenderer = MinecraftForgeClient.getItemRenderer(item, INVENTORY);
if (customRenderer == null)
{
return false;
}
engine.bindTexture(item.getItemSpriteNumber() == 0 ? TextureMap.locationBlocksTexture : TextureMap.locationItemsTexture);
if (customRenderer.shouldUseRenderHelper(INVENTORY, item, INVENTORY_BLOCK))
{
GL11.glPushMatrix();
GL11.glTranslatef(x - 2, y + 3, -3.0F + zLevel);
GL11.glScalef(10F, 10F, 10F);
GL11.glTranslatef(1.0F, 0.5F, 1.0F);
GL11.glScalef(1.0F, 1.0F, -1F);
GL11.glRotatef(210F, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(45F, 0.0F, 1.0F, 0.0F);
if(inColor)
{
int color = Item.itemsList[item.itemID].getColorFromItemStack(item, 0);
float r = (float)(color >> 16 & 0xff) / 255F;
float g = (float)(color >> 8 & 0xff) / 255F;
float b = (float)(color & 0xff) / 255F;
GL11.glColor4f(r, g, b, 1.0F);
}
GL11.glRotatef(-90F, 0.0F, 1.0F, 0.0F);
renderBlocks.useInventoryTint = inColor;
customRenderer.renderItem(INVENTORY, item, renderBlocks);
renderBlocks.useInventoryTint = true;
GL11.glPopMatrix();
}
else
{
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glPushMatrix();
GL11.glTranslatef(x, y, -3.0F + zLevel);
if (inColor)
{
int color = Item.itemsList[item.itemID].getColorFromItemStack(item, 0);
float r = (float)(color >> 16 & 255) / 255.0F;
float g = (float)(color >> 8 & 255) / 255.0F;
float b = (float)(color & 255) / 255.0F;
GL11.glColor4f(r, g, b, 1.0F);
}
customRenderer.renderItem(INVENTORY, item, renderBlocks);
GL11.glPopMatrix();
GL11.glEnable(GL11.GL_LIGHTING);
}
return true;
}
public static void renderEffectOverlay(TextureManager manager, RenderItem render)
{
}
public static void renderEquippedItem(ItemRenderType type, IItemRenderer customRenderer, RenderBlocks renderBlocks, EntityLivingBase entity, ItemStack item)
{
if (customRenderer.shouldUseRenderHelper(type, item, EQUIPPED_BLOCK))
{
GL11.glPushMatrix();
GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
customRenderer.renderItem(type, item, renderBlocks, entity);
GL11.glPopMatrix();
}
else
{
GL11.glPushMatrix();
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
GL11.glTranslatef(0.0F, -0.3F, 0.0F);
GL11.glScalef(1.5F, 1.5F, 1.5F);
GL11.glRotatef(50.0F, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(335.0F, 0.0F, 0.0F, 1.0F);
GL11.glTranslatef(-0.9375F, -0.0625F, 0.0F);
customRenderer.renderItem(type, item, renderBlocks, entity);
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
GL11.glPopMatrix();
}
}
//Optifine Helper Functions u.u, these are here specifically for Optifine
//Note: When using Optfine, these methods are invoked using reflection, which
//incurs a major performance penalty.
public static void orientBedCamera(Minecraft mc, EntityLivingBase entity)
{
int x = MathHelper.floor_double(entity.posX);
int y = MathHelper.floor_double(entity.posY);
int z = MathHelper.floor_double(entity.posZ);
Block block = Block.blocksList[mc.theWorld.getBlockId(x, y, z)];
if (block != null && block.isBed(mc.theWorld, x, y, z, entity))
{
int var12 = block.getBedDirection(mc.theWorld, x, y, z);
GL11.glRotatef((float)(var12 * 90), 0.0F, 1.0F, 0.0F);
}
}
public static boolean onDrawBlockHighlight(RenderGlobal context, EntityPlayer player, MovingObjectPosition target, int subID, ItemStack currentItem, float partialTicks)
{
return MinecraftForge.EVENT_BUS.post(new DrawBlockHighlightEvent(context, player, target, subID, currentItem, partialTicks));
}
public static void dispatchRenderLast(RenderGlobal context, float partialTicks)
{
MinecraftForge.EVENT_BUS.post(new RenderWorldLastEvent(context, partialTicks));
}
public static void onTextureStitchedPre(TextureMap map)
{
MinecraftForge.EVENT_BUS.post(new TextureStitchEvent.Pre(map));
}
public static void onTextureStitchedPost(TextureMap map)
{
MinecraftForge.EVENT_BUS.post(new TextureStitchEvent.Post(map));
FluidRegistry.WATER.setIcons(BlockFluid.getFluidIcon("water_still"), BlockFluid.getFluidIcon("water_flow"));
FluidRegistry.LAVA.setIcons(BlockFluid.getFluidIcon("lava_still"), BlockFluid.getFluidIcon("lava_flow"));
}
/**
* This is added for Optifine's convenience. And to explode if a ModMaker is developing.
* @param texture
*/
public static void onTextureLoadPre(String texture)
{
if (Tessellator.renderingWorldRenderer)
{
String msg = String.format("Warning: Texture %s not preloaded, will cause render glitches!", texture);
System.out.println(msg);
if (Tessellator.class.getPackage() != null)
{
if (Tessellator.class.getPackage().getName().startsWith("net.minecraft."))
{
Minecraft mc = FMLClientHandler.instance().getClient();
if (mc.ingameGUI != null)
{
mc.ingameGUI.getChatGUI().printChatMessage(msg);
}
}
}
}
}
static int renderPass = -1;
public static void setRenderPass(int pass)
{
renderPass = pass;
}
public static ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack, int slotID, ModelBiped _default)
{
ModelBiped modelbiped = itemStack.getItem().getArmorModel(entityLiving, itemStack, slotID);
return modelbiped == null ? _default : modelbiped;
}
static int stencilBits = 0;
public static void createDisplay() throws LWJGLException
{
ImageIO.setUseCache(false); //Disable on-disc stream cache should speed up texture pack reloading.
PixelFormat format = new PixelFormat().withDepthBits(24);
try
{
//TODO: Figure out how to determine the max bits.
Display.create(format.withStencilBits(8));
stencilBits = 8;
}
catch(LWJGLException e)
{
Display.create(format);
stencilBits = 0;
}
}
//This properly moves the domain, if provided, to the front of the string before concatenating
public static String fixDomain(String base, String complex)
{
int idx = complex.indexOf(':');
if (idx == -1)
{
return base + complex;
}
String name = complex.substring(idx + 1, complex.length());
if (idx > 1)
{
String domain = complex.substring(0, idx);
return domain + ':' + base + name;
}
else
{
return base + name;
}
}
public static boolean postMouseEvent()
{
return MinecraftForge.EVENT_BUS.post(new MouseEvent());
}
public static float getOffsetFOV(EntityPlayerSP entity, float fov)
{
FOVUpdateEvent fovUpdateEvent = new FOVUpdateEvent(entity, fov);
MinecraftForge.EVENT_BUS.post(fovUpdateEvent);
return fovUpdateEvent.newfov;
}
private static int skyX, skyZ;
private static boolean skyInit;
private static int skyRGBMultiplier;
public static int getSkyBlendColour(World world, int playerX, int playerZ)
{
if (playerX == skyX && playerZ == skyZ && skyInit)
{
return skyRGBMultiplier;
}
skyInit = true;
int distance = Minecraft.getMinecraft().gameSettings.fancyGraphics ? ForgeDummyContainer.blendRanges[Minecraft.getMinecraft().gameSettings.renderDistance] : 0;
int r = 0;
int g = 0;
int b = 0;
int divider = 0;
for (int x = -distance; x <= distance; ++x)
{
for (int z = -distance; z <= distance; ++z)
{
BiomeGenBase biome = world.getBiomeGenForCoords(playerX + x, playerZ + z);
int colour = biome.getSkyColorByTemp(biome.getFloatTemperature());
r += (colour & 0xFF0000) >> 16;
g += (colour & 0x00FF00) >> 8;
b += colour & 0x0000FF;
divider++;
}
}
int multiplier = (r / divider & 255) << 16 | (g / divider & 255) << 8 | b / divider & 255;
skyX = playerX;
skyZ = playerZ;
skyRGBMultiplier = multiplier;
return skyRGBMultiplier;
}
/**
* Initialization of Forge Renderers.
*/
static
{
FluidRegistry.renderIdFluid = RenderingRegistry.getNextAvailableRenderId();
RenderingRegistry.registerBlockHandler(RenderBlockFluid.instance);
}
}
@@ -0,0 +1,128 @@
package net.minecraftforge.client;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;
import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.client.gui.GuiControls;
import net.minecraft.client.gui.GuiSlot;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.ResourceLocation;
public class GuiControlsScrollPanel extends GuiSlot
{
protected static final ResourceLocation WIDGITS = new ResourceLocation("textures/gui/widgets.png");
private GuiControls controls;
private GameSettings options;
private Minecraft mc;
private String[] message;
private int _mouseX;
private int _mouseY;
private int selected = -1;
public GuiControlsScrollPanel(GuiControls controls, GameSettings options, Minecraft mc)
{
super(mc, controls.width, controls.height, 16, (controls.height - 32) + 4, 25);
this.controls = controls;
this.options = options;
this.mc = mc;
}
@Override
protected int getSize()
{
return options.keyBindings.length;
}
@Override
protected void elementClicked(int i, boolean flag)
{
if (!flag)
{
if (selected == -1)
{
selected = i;
}
else
{
options.setKeyBinding(selected, -100);
selected = -1;
KeyBinding.resetKeyBindingArrayAndHash();
}
}
}
@Override
protected boolean isSelected(int i)
{
return false;
}
@Override
protected void drawBackground() {}
@Override
public void drawScreen(int mX, int mY, float f)
{
_mouseX = mX;
_mouseY = mY;
if (selected != -1 && !Mouse.isButtonDown(0) && Mouse.getDWheel() == 0)
{
if (Mouse.next() && Mouse.getEventButtonState())
{
options.setKeyBinding(selected, -100 + Mouse.getEventButton());
selected = -1;
KeyBinding.resetKeyBindingArrayAndHash();
}
}
super.drawScreen(mX, mY, f);
}
@Override
protected void drawSlot(int index, int xPosition, int yPosition, int l, Tessellator tessellator)
{
int width = 70;
int height = 20;
xPosition -= 20;
boolean flag = _mouseX >= xPosition && _mouseY >= yPosition && _mouseX < xPosition + width && _mouseY < yPosition + height;
int k = (flag ? 2 : 1);
mc.renderEngine.bindTexture(WIDGITS);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
controls.drawTexturedModalRect(xPosition, yPosition, 0, 46 + k * 20, width / 2, height);
controls.drawTexturedModalRect(xPosition + width / 2, yPosition, 200 - width / 2, 46 + k * 20, width / 2, height);
controls.drawString(mc.fontRenderer, options.getKeyBindingDescription(index), xPosition + width + 4, yPosition + 6, 0xFFFFFFFF);
boolean conflict = false;
for (int x = 0; x < options.keyBindings.length; x++)
{
if (x != index && options.keyBindings[x].keyCode == options.keyBindings[index].keyCode)
{
conflict = true;
break;
}
}
String str = (conflict ? EnumChatFormatting.RED : "") + options.getOptionDisplayString(index);
str = (index == selected ? EnumChatFormatting.WHITE + "> " + EnumChatFormatting.YELLOW + "??? " + EnumChatFormatting.WHITE + "<" : str);
controls.drawCenteredString(mc.fontRenderer, str, xPosition + (width / 2), yPosition + (height - 8) / 2, 0xFFFFFFFF);
}
public boolean keyTyped(char c, int i)
{
if (selected != -1)
{
options.setKeyBinding(selected, i);
selected = -1;
KeyBinding.resetKeyBindingArrayAndHash();
return false;
}
return true;
}
}
@@ -0,0 +1,880 @@
package net.minecraftforge.client;
import static net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType.*;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiIngame;
import net.minecraft.client.gui.GuiPlayerInfo;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.multiplayer.NetClientHandler;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.crash.CallableMinecraftVersion;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.attributes.AttributeInstance;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.potion.Potion;
import net.minecraft.scoreboard.Score;
import net.minecraft.scoreboard.ScoreObjective;
import net.minecraft.scoreboard.ScorePlayerTeam;
import net.minecraft.util.Direction;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.FoodStats;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;
import net.minecraft.util.StringUtils;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.common.MinecraftForge;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import cpw.mods.fml.common.FMLCommonHandler;
public class GuiIngameForge extends GuiIngame
{
private static final ResourceLocation VIGNETTE = new ResourceLocation("textures/misc/vignette.png");
private static final ResourceLocation WIDGITS = new ResourceLocation("textures/gui/widgets.png");
private static final ResourceLocation PUMPKIN_BLUR = new ResourceLocation("textures/misc/pumpkinblur.png");
private static final int WHITE = 0xFFFFFF;
//Flags to toggle the rendering of certain aspects of the HUD, valid conditions
//must be met for them to render normally. If those conditions are met, but this flag
//is false, they will not be rendered.
public static boolean renderHelmet = true;
public static boolean renderPortal = true;
public static boolean renderHotbar = true;
public static boolean renderCrosshairs = true;
public static boolean renderBossHealth = true;
public static boolean renderHealth = true;
public static boolean renderArmor = true;
public static boolean renderFood = true;
public static boolean renderHealthMount = true;
public static boolean renderAir = true;
public static boolean renderExperiance = true;
public static boolean renderJumpBar = true;
public static boolean renderObjective = true;
public static int left_height = 39;
public static int right_height = 39;
private ScaledResolution res = null;
private FontRenderer fontrenderer = null;
private RenderGameOverlayEvent eventParent;
private static final String MC_VERSION = (new CallableMinecraftVersion(null)).minecraftVersion();
public GuiIngameForge(Minecraft mc)
{
super(mc);
}
@Override
public void renderGameOverlay(float partialTicks, boolean hasScreen, int mouseX, int mouseY)
{
res = new ScaledResolution(mc.gameSettings, mc.displayWidth, mc.displayHeight);
eventParent = new RenderGameOverlayEvent(partialTicks, res, mouseX, mouseY);
int width = res.getScaledWidth();
int height = res.getScaledHeight();
renderHealthMount = mc.thePlayer.ridingEntity instanceof EntityLivingBase;
renderFood = mc.thePlayer.ridingEntity == null;
renderJumpBar = mc.thePlayer.isRidingHorse();
right_height = 39;
left_height = 39;
if (pre(ALL)) return;
fontrenderer = mc.fontRenderer;
mc.entityRenderer.setupOverlayRendering();
GL11.glEnable(GL11.GL_BLEND);
if (Minecraft.isFancyGraphicsEnabled())
{
renderVignette(mc.thePlayer.getBrightness(partialTicks), width, height);
}
else
{
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
}
if (renderHelmet) renderHelmet(res, partialTicks, hasScreen, mouseX, mouseY);
if (renderPortal && !mc.thePlayer.isPotionActive(Potion.confusion))
{
renderPortal(width, height, partialTicks);
}
if (!mc.playerController.enableEverythingIsScrewedUpMode())
{
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
zLevel = -90.0F;
rand.setSeed((long)(updateCounter * 312871));
if (renderCrosshairs) renderCrosshairs(width, height);
if (renderBossHealth) renderBossHealth();
if (this.mc.playerController.shouldDrawHUD())
{
if (renderHealth) renderHealth(width, height);
if (renderArmor) renderArmor(width, height);
if (renderFood) renderFood(width, height);
if (renderHealthMount) renderHealthMount(width, height);
if (renderAir) renderAir(width, height);
}
if (renderHotbar) renderHotbar(width, height, partialTicks);
}
if (renderJumpBar)
{
renderJumpBar(width, height);
}
else if (renderExperiance)
{
renderExperience(width, height);
}
renderSleepFade(width, height);
renderToolHightlight(width, height);
renderHUDText(width, height);
renderRecordOverlay(width, height, partialTicks);
ScoreObjective objective = mc.theWorld.getScoreboard().func_96539_a(1);
if (renderObjective && objective != null)
{
this.func_96136_a(objective, height, width, fontrenderer);
}
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glDisable(GL11.GL_ALPHA_TEST);
renderChat(width, height);
renderPlayerList(width, height);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_ALPHA_TEST);
post(ALL);
}
public ScaledResolution getResolution()
{
return res;
}
protected void renderHotbar(int width, int height, float partialTicks)
{
if (pre(HOTBAR)) return;
mc.mcProfiler.startSection("actionBar");
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
mc.renderEngine.bindTexture(WIDGITS);
InventoryPlayer inv = mc.thePlayer.inventory;
drawTexturedModalRect(width / 2 - 91, height - 22, 0, 0, 182, 22);
drawTexturedModalRect(width / 2 - 91 - 1 + inv.currentItem * 20, height - 22 - 1, 0, 22, 24, 22);
GL11.glDisable(GL11.GL_BLEND);
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
RenderHelper.enableGUIStandardItemLighting();
for (int i = 0; i < 9; ++i)
{
int x = width / 2 - 90 + i * 20 + 2;
int z = height - 16 - 3;
renderInventorySlot(i, x, z, partialTicks);
}
RenderHelper.disableStandardItemLighting();
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
mc.mcProfiler.endSection();
post(HOTBAR);
}
protected void renderCrosshairs(int width, int height)
{
if (pre(CROSSHAIRS)) return;
bind(Gui.icons);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_ONE_MINUS_DST_COLOR, GL11.GL_ONE_MINUS_SRC_COLOR);
drawTexturedModalRect(width / 2 - 7, height / 2 - 7, 0, 0, 16, 16);
GL11.glDisable(GL11.GL_BLEND);
post(CROSSHAIRS);
}
@Override
protected void renderBossHealth()
{
if (pre(BOSSHEALTH)) return;
mc.mcProfiler.startSection("bossHealth");
super.renderBossHealth();
mc.mcProfiler.endSection();
post(BOSSHEALTH);
}
private void renderHelmet(ScaledResolution res, float partialTicks, boolean hasScreen, int mouseX, int mouseY)
{
if (pre(HELMET)) return;
ItemStack itemstack = this.mc.thePlayer.inventory.armorItemInSlot(3);
if (this.mc.gameSettings.thirdPersonView == 0 && itemstack != null && itemstack.getItem() != null)
{
if (itemstack.itemID == Block.pumpkin.blockID)
{
renderPumpkinBlur(res.getScaledWidth(), res.getScaledHeight());
}
else
{
itemstack.getItem().renderHelmetOverlay(itemstack, mc.thePlayer, res, partialTicks, hasScreen, mouseX, mouseY);
}
}
post(HELMET);
}
protected void renderArmor(int width, int height)
{
if (pre(ARMOR)) return;
mc.mcProfiler.startSection("armor");
int left = width / 2 - 91;
int top = height - left_height;
int level = ForgeHooks.getTotalArmorValue(mc.thePlayer);
for (int i = 1; level > 0 && i < 20; i += 2)
{
if (i < level)
{
drawTexturedModalRect(left, top, 34, 9, 9, 9);
}
else if (i == level)
{
drawTexturedModalRect(left, top, 25, 9, 9, 9);
}
else if (i > level)
{
drawTexturedModalRect(left, top, 16, 9, 9, 9);
}
left += 8;
}
left_height += 10;
mc.mcProfiler.endSection();
post(ARMOR);
}
protected void renderPortal(int width, int height, float partialTicks)
{
if (pre(PORTAL)) return;
float f1 = mc.thePlayer.prevTimeInPortal + (mc.thePlayer.timeInPortal - mc.thePlayer.prevTimeInPortal) * partialTicks;
if (f1 > 0.0F)
{
func_130015_b(f1, width, height);
}
post(PORTAL);
}
protected void renderAir(int width, int height)
{
if (pre(AIR)) return;
mc.mcProfiler.startSection("air");
int left = width / 2 + 91;
int top = height - right_height;
if (mc.thePlayer.isInsideOfMaterial(Material.water))
{
int air = mc.thePlayer.getAir();
int full = MathHelper.ceiling_double_int((double)(air - 2) * 10.0D / 300.0D);
int partial = MathHelper.ceiling_double_int((double)air * 10.0D / 300.0D) - full;
for (int i = 0; i < full + partial; ++i)
{
drawTexturedModalRect(left - i * 8 - 9, top, (i < full ? 16 : 25), 18, 9, 9);
}
right_height += 10;
}
mc.mcProfiler.endSection();
post(AIR);
}
public void renderHealth(int width, int height)
{
bind(icons);
if (pre(HEALTH)) return;
mc.mcProfiler.startSection("health");
boolean highlight = mc.thePlayer.hurtResistantTime / 3 % 2 == 1;
if (mc.thePlayer.hurtResistantTime < 10)
{
highlight = false;
}
AttributeInstance attrMaxHealth = this.mc.thePlayer.getEntityAttribute(SharedMonsterAttributes.maxHealth);
int health = MathHelper.ceiling_float_int(mc.thePlayer.getHealth());
int healthLast = MathHelper.ceiling_float_int(mc.thePlayer.prevHealth);
float healthMax = (float)attrMaxHealth.getAttributeValue();
float absorb = this.mc.thePlayer.getAbsorptionAmount();
int healthRows = MathHelper.ceiling_float_int((healthMax + absorb) / 2.0F / 10.0F);
int rowHeight = Math.max(10 - (healthRows - 2), 3);
this.rand.setSeed((long)(updateCounter * 312871));
int left = width / 2 - 91;
int top = height - left_height;
left_height += (healthRows * rowHeight);
if (rowHeight != 10) left_height += 10 - rowHeight;
int regen = -1;
if (mc.thePlayer.isPotionActive(Potion.regeneration))
{
regen = updateCounter % 25;
}
final int TOP = 9 * (mc.theWorld.getWorldInfo().isHardcoreModeEnabled() ? 5 : 0);
final int BACKGROUND = (highlight ? 25 : 16);
int MARGIN = 16;
if (mc.thePlayer.isPotionActive(Potion.poison)) MARGIN += 36;
else if (mc.thePlayer.isPotionActive(Potion.wither)) MARGIN += 72;
float absorbRemaining = absorb;
for (int i = MathHelper.ceiling_float_int((healthMax + absorb) / 2.0F) - 1; i >= 0; --i)
{
int b0 = (highlight ? 1 : 0);
int row = MathHelper.ceiling_float_int((float)(i + 1) / 10.0F) - 1;
int x = left + i % 10 * 8;
int y = top - row * rowHeight;
if (health <= 4) y += rand.nextInt(2);
if (i == regen) y -= 2;
drawTexturedModalRect(x, y, BACKGROUND, TOP, 9, 9);
if (highlight)
{
if (i * 2 + 1 < healthLast)
drawTexturedModalRect(x, y, MARGIN + 54, TOP, 9, 9); //6
else if (i * 2 + 1 == healthLast)
drawTexturedModalRect(x, y, MARGIN + 63, TOP, 9, 9); //7
}
if (absorbRemaining > 0.0F)
{
if (absorbRemaining == absorb && absorb % 2.0F == 1.0F)
drawTexturedModalRect(x, y, MARGIN + 153, TOP, 9, 9); //17
else
drawTexturedModalRect(x, y, MARGIN + 144, TOP, 9, 9); //16
absorbRemaining -= 2.0F;
}
else
{
if (i * 2 + 1 < health)
drawTexturedModalRect(x, y, MARGIN + 36, TOP, 9, 9); //4
else if (i * 2 + 1 == health)
drawTexturedModalRect(x, y, MARGIN + 45, TOP, 9, 9); //5
}
}
mc.mcProfiler.endSection();
post(HEALTH);
}
public void renderFood(int width, int height)
{
if (pre(FOOD)) return;
mc.mcProfiler.startSection("food");
int left = width / 2 + 91;
int top = height - right_height;
right_height += 10;
boolean unused = false;// Unused flag in vanilla, seems to be part of a 'fade out' mechanic
FoodStats stats = mc.thePlayer.getFoodStats();
int level = stats.getFoodLevel();
int levelLast = stats.getPrevFoodLevel();
for (int i = 0; i < 10; ++i)
{
int idx = i * 2 + 1;
int x = left - i * 8 - 9;
int y = top;
int icon = 16;
byte backgound = 0;
if (mc.thePlayer.isPotionActive(Potion.hunger))
{
icon += 36;
backgound = 13;
}
if (unused) backgound = 1; //Probably should be a += 1 but vanilla never uses this
if (mc.thePlayer.getFoodStats().getSaturationLevel() <= 0.0F && updateCounter % (level * 3 + 1) == 0)
{
y = top + (rand.nextInt(3) - 1);
}
drawTexturedModalRect(x, y, 16 + backgound * 9, 27, 9, 9);
if (unused)
{
if (idx < levelLast)
drawTexturedModalRect(x, y, icon + 54, 27, 9, 9);
else if (idx == levelLast)
drawTexturedModalRect(x, y, icon + 63, 27, 9, 9);
}
if (idx < level)
drawTexturedModalRect(x, y, icon + 36, 27, 9, 9);
else if (idx == level)
drawTexturedModalRect(x, y, icon + 45, 27, 9, 9);
}
mc.mcProfiler.endSection();
post(FOOD);
}
protected void renderSleepFade(int width, int height)
{
if (mc.thePlayer.getSleepTimer() > 0)
{
mc.mcProfiler.startSection("sleep");
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glDisable(GL11.GL_ALPHA_TEST);
int sleepTime = mc.thePlayer.getSleepTimer();
float opacity = (float)sleepTime / 100.0F;
if (opacity > 1.0F)
{
opacity = 1.0F - (float)(sleepTime - 100) / 10.0F;
}
int color = (int)(220.0F * opacity) << 24 | 1052704;
drawRect(0, 0, width, height, color);
GL11.glEnable(GL11.GL_ALPHA_TEST);
GL11.glEnable(GL11.GL_DEPTH_TEST);
mc.mcProfiler.endSection();
}
}
protected void renderExperience(int width, int height)
{
bind(icons);
if (pre(EXPERIENCE)) return;
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
if (mc.playerController.func_78763_f())
{
mc.mcProfiler.startSection("expBar");
int cap = this.mc.thePlayer.xpBarCap();
int left = width / 2 - 91;
if (cap > 0)
{
short barWidth = 182;
int filled = (int)(mc.thePlayer.experience * (float)(barWidth + 1));
int top = height - 32 + 3;
drawTexturedModalRect(left, top, 0, 64, barWidth, 5);
if (filled > 0)
{
drawTexturedModalRect(left, top, 0, 69, filled, 5);
}
}
this.mc.mcProfiler.endSection();
if (mc.playerController.func_78763_f() && mc.thePlayer.experienceLevel > 0)
{
mc.mcProfiler.startSection("expLevel");
boolean flag1 = false;
int color = flag1 ? 16777215 : 8453920;
String text = "" + mc.thePlayer.experienceLevel;
int x = (width - fontrenderer.getStringWidth(text)) / 2;
int y = height - 31 - 4;
fontrenderer.drawString(text, x + 1, y, 0);
fontrenderer.drawString(text, x - 1, y, 0);
fontrenderer.drawString(text, x, y + 1, 0);
fontrenderer.drawString(text, x, y - 1, 0);
fontrenderer.drawString(text, x, y, color);
mc.mcProfiler.endSection();
}
}
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
post(EXPERIENCE);
}
protected void renderJumpBar(int width, int height)
{
bind(icons);
if (pre(JUMPBAR)) return;
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
mc.mcProfiler.startSection("jumpBar");
float charge = mc.thePlayer.getHorseJumpPower();
final int barWidth = 182;
int x = (width / 2) - (barWidth / 2);
int filled = (int)(charge * (float)(barWidth + 1));
int top = height - 32 + 3;
drawTexturedModalRect(x, top, 0, 84, barWidth, 5);
if (filled > 0)
{
this.drawTexturedModalRect(x, top, 0, 89, filled, 5);
}
mc.mcProfiler.endSection();
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
post(JUMPBAR);
}
protected void renderToolHightlight(int width, int height)
{
if (this.mc.gameSettings.heldItemTooltips)
{
mc.mcProfiler.startSection("toolHighlight");
if (this.remainingHighlightTicks > 0 && this.highlightingItemStack != null)
{
String name = this.highlightingItemStack.getDisplayName();
int opacity = (int)((float)this.remainingHighlightTicks * 256.0F / 10.0F);
if (opacity > 255) opacity = 255;
if (opacity > 0)
{
int y = height - 59;
if (!mc.playerController.shouldDrawHUD()) y += 14;
GL11.glPushMatrix();
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
FontRenderer font = highlightingItemStack.getItem().getFontRenderer(highlightingItemStack);
if (font != null)
{
int x = (width - font.getStringWidth(name)) / 2;
font.drawStringWithShadow(name, x, y, WHITE | (opacity << 24));
}
else
{
int x = (width - fontrenderer.getStringWidth(name)) / 2;
fontrenderer.drawStringWithShadow(name, x, y, WHITE | (opacity << 24));
}
GL11.glDisable(GL11.GL_BLEND);
GL11.glPopMatrix();
}
}
mc.mcProfiler.endSection();
}
}
protected void renderHUDText(int width, int height)
{
mc.mcProfiler.startSection("forgeHudText");
ArrayList<String> left = new ArrayList<String>();
ArrayList<String> right = new ArrayList<String>();
if (mc.isDemo())
{
long time = mc.theWorld.getTotalWorldTime();
if (time >= 120500L)
{
right.add(StatCollector.translateToLocal("demo.demoExpired"));
}
else
{
right.add(String.format(StatCollector.translateToLocal("demo.remainingTime"), StringUtils.ticksToElapsedTime((int)(120500L - time))));
}
}
if (this.mc.gameSettings.showDebugInfo)
{
mc.mcProfiler.startSection("debug");
GL11.glPushMatrix();
left.add("Minecraft " + MC_VERSION + " (" + this.mc.debug + ")");
left.add(mc.debugInfoRenders());
left.add(mc.getEntityDebug());
left.add(mc.debugInfoEntities());
left.add(mc.getWorldProviderName());
left.add(null); //Spacer
long max = Runtime.getRuntime().maxMemory();
long total = Runtime.getRuntime().totalMemory();
long free = Runtime.getRuntime().freeMemory();
long used = total - free;
right.add("Used memory: " + used * 100L / max + "% (" + used / 1024L / 1024L + "MB) of " + max / 1024L / 1024L + "MB");
right.add("Allocated memory: " + total * 100L / max + "% (" + total / 1024L / 1024L + "MB)");
int x = MathHelper.floor_double(mc.thePlayer.posX);
int y = MathHelper.floor_double(mc.thePlayer.posY);
int z = MathHelper.floor_double(mc.thePlayer.posZ);
float yaw = mc.thePlayer.rotationYaw;
int heading = MathHelper.floor_double((double)(mc.thePlayer.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3;
left.add(String.format("x: %.5f (%d) // c: %d (%d)", mc.thePlayer.posX, x, x >> 4, x & 15));
left.add(String.format("y: %.3f (feet pos, %.3f eyes pos)", mc.thePlayer.boundingBox.minY, mc.thePlayer.posY));
left.add(String.format("z: %.5f (%d) // c: %d (%d)", mc.thePlayer.posZ, z, z >> 4, z & 15));
left.add(String.format("f: %d (%s) / %f", heading, Direction.directions[heading], MathHelper.wrapAngleTo180_float(yaw)));
if (mc.theWorld != null && mc.theWorld.blockExists(x, y, z))
{
Chunk chunk = this.mc.theWorld.getChunkFromBlockCoords(x, z);
left.add(String.format("lc: %d b: %s bl: %d sl: %d rl: %d",
chunk.getTopFilledSegment() + 15,
chunk.getBiomeGenForWorldCoords(x & 15, z & 15, mc.theWorld.getWorldChunkManager()).biomeName,
chunk.getSavedLightValue(EnumSkyBlock.Block, x & 15, y, z & 15),
chunk.getSavedLightValue(EnumSkyBlock.Sky, x & 15, y, z & 15),
chunk.getBlockLightValue(x & 15, y, z & 15, 0)));
}
else
{
left.add(null);
}
left.add(String.format("ws: %.3f, fs: %.3f, g: %b, fl: %d", mc.thePlayer.capabilities.getWalkSpeed(), mc.thePlayer.capabilities.getFlySpeed(), mc.thePlayer.onGround, mc.theWorld.getHeightValue(x, z)));
right.add(null);
for (String s : FMLCommonHandler.instance().getBrandings().subList(1, FMLCommonHandler.instance().getBrandings().size()))
{
right.add(s);
}
GL11.glPopMatrix();
mc.mcProfiler.endSection();
}
RenderGameOverlayEvent.Text event = new RenderGameOverlayEvent.Text(eventParent, left, right);
if (!MinecraftForge.EVENT_BUS.post(event))
{
for (int x = 0; x < left.size(); x++)
{
String msg = left.get(x);
if (msg == null) continue;
fontrenderer.drawStringWithShadow(msg, 2, 2 + x * 10, WHITE);
}
for (int x = 0; x < right.size(); x++)
{
String msg = right.get(x);
if (msg == null) continue;
int w = fontrenderer.getStringWidth(msg);
fontrenderer.drawStringWithShadow(msg, width - w - 10, 2 + x * 10, WHITE);
}
}
mc.mcProfiler.endSection();
post(TEXT);
}
protected void renderRecordOverlay(int width, int height, float partialTicks)
{
if (recordPlayingUpFor > 0)
{
mc.mcProfiler.startSection("overlayMessage");
float hue = (float)recordPlayingUpFor - partialTicks;
int opacity = (int)(hue * 256.0F / 20.0F);
if (opacity > 255) opacity = 255;
if (opacity > 0)
{
GL11.glPushMatrix();
GL11.glTranslatef((float)(width / 2), (float)(height - 48), 0.0F);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
int color = (recordIsPlaying ? Color.HSBtoRGB(hue / 50.0F, 0.7F, 0.6F) & WHITE : WHITE);
fontrenderer.drawString(recordPlaying, -fontrenderer.getStringWidth(recordPlaying) / 2, -4, color | (opacity << 24));
GL11.glDisable(GL11.GL_BLEND);
GL11.glPopMatrix();
}
mc.mcProfiler.endSection();
}
}
protected void renderChat(int width, int height)
{
mc.mcProfiler.startSection("chat");
RenderGameOverlayEvent.Chat event = new RenderGameOverlayEvent.Chat(eventParent, 0, height - 48);
if (MinecraftForge.EVENT_BUS.post(event)) return;
GL11.glPushMatrix();
GL11.glTranslatef((float)event.posX, (float)event.posY, 0.0F);
persistantChatGUI.drawChat(updateCounter);
GL11.glPopMatrix();
post(CHAT);
mc.mcProfiler.endSection();
}
protected void renderPlayerList(int width, int height)
{
ScoreObjective scoreobjective = this.mc.theWorld.getScoreboard().func_96539_a(0);
NetClientHandler handler = mc.thePlayer.sendQueue;
if (mc.gameSettings.keyBindPlayerList.pressed && (!mc.isIntegratedServerRunning() || handler.playerInfoList.size() > 1 || scoreobjective != null))
{
if (pre(PLAYER_LIST)) return;
this.mc.mcProfiler.startSection("playerList");
List players = handler.playerInfoList;
int maxPlayers = handler.currentServerMaxPlayers;
int rows = maxPlayers;
int columns = 1;
for (columns = 1; rows > 20; rows = (maxPlayers + columns - 1) / columns)
{
columns++;
}
int columnWidth = 300 / columns;
if (columnWidth > 150)
{
columnWidth = 150;
}
int left = (width - columns * columnWidth) / 2;
byte border = 10;
drawRect(left - 1, border - 1, left + columnWidth * columns, border + 9 * rows, Integer.MIN_VALUE);
for (int i = 0; i < maxPlayers; i++)
{
int xPos = left + i % columns * columnWidth;
int yPos = border + i / columns * 9;
drawRect(xPos, yPos, xPos + columnWidth - 1, yPos + 8, 553648127);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glEnable(GL11.GL_ALPHA_TEST);
if (i < players.size())
{
GuiPlayerInfo player = (GuiPlayerInfo)players.get(i);
ScorePlayerTeam team = mc.theWorld.getScoreboard().getPlayersTeam(player.name);
String displayName = ScorePlayerTeam.formatPlayerName(team, player.name);
fontrenderer.drawStringWithShadow(displayName, xPos, yPos, 16777215);
if (scoreobjective != null)
{
int endX = xPos + fontrenderer.getStringWidth(displayName) + 5;
int maxX = xPos + columnWidth - 12 - 5;
if (maxX - endX > 5)
{
Score score = scoreobjective.getScoreboard().func_96529_a(player.name, scoreobjective);
String scoreDisplay = EnumChatFormatting.YELLOW + "" + score.getScorePoints();
fontrenderer.drawStringWithShadow(scoreDisplay, maxX - fontrenderer.getStringWidth(scoreDisplay), yPos, 16777215);
}
}
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
mc.getTextureManager().bindTexture(Gui.icons);
int pingIndex = 4;
int ping = player.responseTime;
if (ping < 0) pingIndex = 5;
else if (ping < 150) pingIndex = 0;
else if (ping < 300) pingIndex = 1;
else if (ping < 600) pingIndex = 2;
else if (ping < 1000) pingIndex = 3;
zLevel += 100.0F;
drawTexturedModalRect(xPos + columnWidth - 12, yPos, 0, 176 + pingIndex * 8, 10, 8);
zLevel -= 100.0F;
}
}
post(PLAYER_LIST);
}
}
protected void renderHealthMount(int width, int height)
{
Entity tmp = mc.thePlayer.ridingEntity;
if (!(tmp instanceof EntityLivingBase)) return;
bind(icons);
if (pre(HEALTHMOUNT)) return;
boolean unused = false;
int left_align = width / 2 + 91;
mc.mcProfiler.endStartSection("mountHealth");
EntityLivingBase mount = (EntityLivingBase)tmp;
int health = (int)Math.ceil((double)mount.getHealth());
float healthMax = mount.getMaxHealth();
int hearts = (int)(healthMax + 0.5F) / 2;
if (hearts > 30) hearts = 30;
final int MARGIN = 52;
final int BACKGROUND = MARGIN + (unused ? 1 : 0);
final int HALF = MARGIN + 45;
final int FULL = MARGIN + 36;
for (int heart = 0; hearts > 0; heart += 20)
{
int top = height - right_height;
int rowCount = Math.min(hearts, 10);
hearts -= rowCount;
for (int i = 0; i < rowCount; ++i)
{
int x = left_align - i * 8 - 9;
drawTexturedModalRect(x, top, BACKGROUND, 9, 9, 9);
if (i * 2 + 1 + heart < health)
drawTexturedModalRect(x, top, FULL, 9, 9, 9);
else if (i * 2 + 1 + heart == health)
drawTexturedModalRect(x, top, HALF, 9, 9, 9);
}
right_height += 10;
}
post(HEALTHMOUNT);
}
//Helper macros
private boolean pre(ElementType type)
{
return MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Pre(eventParent, type));
}
private void post(ElementType type)
{
MinecraftForge.EVENT_BUS.post(new RenderGameOverlayEvent.Post(eventParent, type));
}
private void bind(ResourceLocation res)
{
mc.getTextureManager().bindTexture(res);
}
}
@@ -0,0 +1,137 @@
package net.minecraftforge.client;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.EntityLiving;
import net.minecraft.item.ItemStack;
import net.minecraft.client.renderer.RenderBlocks;
public interface IItemRenderer
{
public enum ItemRenderType
{
/**
* Called to render an in-world item, e.g. one that has been thrown or
* dropped. The appropriate OpenGL transformations and scaling have already
* been applied, so Tessellator location (0,0,0) is the center of the
* EntityItem.
*
* Data parameters:
* RenderBlocks render - The RenderBlocks instance
* EntityItem entity - The in-world item to be rendered
*/
ENTITY,
/**
* Called to render an item currently held in-hand by a living entity. If
* rendering as a 3D block, the item will be rotated to a 45-degree angle.
* To render a 2D texture with some thickness (like default items), see
* net.minecraft.src.ItemRenderer. In either case, rendering should be done
* in local coordinates from (0,0,0)-(1,1,1).
*
* Data parameters:
* RenderBlocks render - The RenderBlocks instance
* EntityLiving entity - The entity holding this item
*/
EQUIPPED,
/**
* Called to render an item currently held in-hand by a living entity in
* first person. If rendering as a 3D block, the item will be rotated to a
* 45-degree angle. To render a 2D texture with some thickness, see
* net.minecraft.src.ItemRenderer. In either case, rendering should be done
* in local coordinates from (0,0,0)-(1,1,1).
*
* Data parameters:
* RenderBlocks render - The RenderBlocks instance
* EntityLiving entity - The entity holding this item
*/
EQUIPPED_FIRST_PERSON,
/**
* Called to render an item in a GUI inventory slot. If rendering as a 3D
* block, the appropriate OpenGL translations and scaling have already been
* applied, and the rendering should be done in local coordinates from
* (0,0,0)-(1,1,1). If rendering as a 2D texture, the rendering should be in
* GUI pixel coordinates from (0, 0, 0)-(16, 16, 0).
*
* Data parameters:
* RenderBlocks render - The RenderBlocks instance
*/
INVENTORY,
/**
* The render type used for when a ItemMap is rendered in first person,
* All appropriate rotations have been applied, and the player's hands,
* and the map BG are already rendered.
*
* Data Parameters:
* EntityPlayer player - The player holding the map
* RenderEngine engine - The RenderEngine instance
* MapData mapData - The map data
*/
FIRST_PERSON_MAP
}
public enum ItemRendererHelper
{
/**
* Determines if a rotation effect should be used when rendering an
* EntityItem, like most default blocks do.
*/
ENTITY_ROTATION,
/**
* Determines if an up-and-down bobbing effect should be used when
* rendering an EntityItem, like most default items do.
*/
ENTITY_BOBBING,
/**
* Determines if the currently equipped item should be rendered as a 3D
* block or as a 2D texture.
*/
EQUIPPED_BLOCK,
/**
* Determines if the item should equate to a block that has
* RenderBlocks.renderItemIn3d return true
*/
BLOCK_3D,
/**
* Determines if the item should be rendered in GUI inventory slots as a 3D
* block or as a 2D texture.
*/
INVENTORY_BLOCK
}
/**
* Checks if this renderer should handle a specific item's render type
* @param item The item we are trying to render
* @param type A render type to check if this renderer handles
* @return true if this renderer should handle the given render type,
* otherwise false
*/
public boolean handleRenderType(ItemStack item, ItemRenderType type);
/**
* Checks if certain helper functionality should be executed for this renderer.
* See ItemRendererHelper for more info
*
* @param type The render type
* @param item The ItemStack being rendered
* @param helper The type of helper functionality to be ran
* @return True to run the helper functionality, false to not.
*/
public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper);
/**
* Called to do the actual rendering, see ItemRenderType for details on when specific
* types are run, and what extra data is passed into the data parameter.
*
* @param type The render type
* @param item The ItemStack being rendered
* @param data Extra Type specific data
*/
public void renderItem(ItemRenderType type, ItemStack item, Object... data);
}
@@ -0,0 +1,17 @@
/*
* This software is provided under the terms of the Minecraft Forge Public
* License v1.0.
*/
package net.minecraftforge.client;
public interface IRenderContextHandler
{
/** Run before the specified rendering context.
*/
public void beforeRenderContext();
/** Run after the specified rendering context.
*/
public void afterRenderContext();
}
@@ -0,0 +1,12 @@
package net.minecraftforge.client;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
public abstract class IRenderHandler
{
@SideOnly(Side.CLIENT)
public abstract void render(float partialTicks, WorldClient world, Minecraft mc);
}
@@ -0,0 +1,92 @@
/**
* This software is provided under the terms of the Minecraft Forge Public
* License v1.0.
*/
package net.minecraftforge.client;
import java.util.BitSet;
import org.lwjgl.opengl.Display;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.world.World;
import net.minecraftforge.client.IItemRenderer.ItemRenderType;
import net.minecraftforge.common.MinecraftForge;
public class MinecraftForgeClient
{
private static IItemRenderer[] customItemRenderers = new IItemRenderer[Item.itemsList.length];
/**
* Register a custom renderer for a specific item. This can be used to
* render the item in-world as an EntityItem, when the item is equipped, or
* when the item is in an inventory slot.
* @param itemID The item ID (shifted index) to handle rendering.
* @param renderer The IItemRenderer interface that handles rendering for
* this item.
*/
public static void registerItemRenderer(int itemID, IItemRenderer renderer)
{
customItemRenderers[itemID] = renderer;
}
public static IItemRenderer getItemRenderer(ItemStack item, ItemRenderType type)
{
IItemRenderer renderer = customItemRenderers[item.itemID];
if (renderer != null && renderer.handleRenderType(item, type))
{
return customItemRenderers[item.itemID];
}
return null;
}
public static int getRenderPass()
{
return ForgeHooksClient.renderPass;
}
public static int getStencilBits()
{
return ForgeHooksClient.stencilBits;
}
private static BitSet stencilBits = new BitSet(getStencilBits());
static
{
stencilBits.set(0,getStencilBits());
}
/**
* Reserve a stencil bit for use in rendering
*
* @return A bit or -1 if no further stencil bits are available
*/
public static int reserveStencilBit()
{
int bit = stencilBits.nextSetBit(0);
if (bit >= 0)
{
stencilBits.clear(bit);
}
return bit;
}
/**
* Release the stencil bit for other use
*
* @param bit The bit from {@link #reserveStencilBit()}
*/
public static void releaseStencilBit(int bit)
{
if (bit >= 0 && bit < getStencilBits())
{
stencilBits.set(bit);
}
}
}
@@ -0,0 +1,14 @@
package net.minecraftforge.client.event;
import net.minecraftforge.event.Cancelable;
import net.minecraftforge.event.Event;
@Cancelable
public class ClientChatReceivedEvent extends Event
{
public String message;
public ClientChatReceivedEvent(String message)
{
this.message = message;
}
}
@@ -0,0 +1,30 @@
package net.minecraftforge.client.event;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraftforge.event.Cancelable;
import net.minecraftforge.event.Event;
@Cancelable
public class DrawBlockHighlightEvent extends Event
{
public final RenderGlobal context;
public final EntityPlayer player;
public final MovingObjectPosition target;
public final int subID;
public final ItemStack currentItem;
public final float partialTicks;
public DrawBlockHighlightEvent(RenderGlobal context, EntityPlayer player, MovingObjectPosition target, int subID, ItemStack currentItem, float partialTicks)
{
this.context = context;
this.player = player;
this.target = target;
this.subID = subID;
this.currentItem = currentItem;
this.partialTicks= partialTicks;
}
}
@@ -0,0 +1,23 @@
package net.minecraftforge.client.event;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraftforge.event.Event;
/**
* Author: MachineMuse (Claire Semple)
* Created: 6:07 PM, 9/5/13
*/
public class FOVUpdateEvent extends Event
{
public final EntityPlayerSP entity;
public final float fov;
public float newfov;
public FOVUpdateEvent(EntityPlayerSP entity, float fov)
{
this.entity = entity;
this.fov = fov;
this.newfov = fov;
}
}
@@ -0,0 +1,22 @@
package net.minecraftforge.client.event;
import net.minecraft.client.gui.GuiScreen;
import net.minecraftforge.event.Cancelable;
import net.minecraftforge.event.Event;
/**
* This event is called before any Gui will open.
* If you don't want this to happen, cancel the event.
* If you want to override this Gui, simply set the gui variable to your own Gui.
*
* @author jk-5
*/
@Cancelable
public class GuiOpenEvent extends Event
{
public GuiScreen gui;
public GuiOpenEvent(GuiScreen gui)
{
this.gui = gui;
}
}
@@ -0,0 +1,34 @@
package net.minecraftforge.client.event;
import net.minecraftforge.event.Cancelable;
import net.minecraftforge.event.Event;
import org.lwjgl.input.Mouse;
/**
* Author: MachineMuse (Claire Semple)
* Created: 2:46 PM, 9/4/13
*/
@Cancelable
public class MouseEvent extends Event
{
public final int x;
public final int y;
public final int dx;
public final int dy;
public final int dwheel;
public final int button;
public final boolean buttonstate;
public final long nanoseconds;
public MouseEvent()
{
this.x = Mouse.getEventX();
this.y = Mouse.getEventY();
this.dx = Mouse.getEventDX();
this.dy = Mouse.getEventDY();
this.dwheel = Mouse.getEventDWheel();
this.button = Mouse.getEventButton();
this.buttonstate = Mouse.getEventButtonState();
this.nanoseconds = Mouse.getEventNanoseconds();
}
}
@@ -0,0 +1,97 @@
package net.minecraftforge.client.event;
import java.util.ArrayList;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraftforge.event.Cancelable;
import net.minecraftforge.event.Event;
@Cancelable
public class RenderGameOverlayEvent extends Event
{
public static enum ElementType
{
ALL,
HELMET,
PORTAL,
CROSSHAIRS,
BOSSHEALTH,
ARMOR,
HEALTH,
FOOD,
AIR,
HOTBAR,
EXPERIENCE,
TEXT,
HEALTHMOUNT,
JUMPBAR,
CHAT,
PLAYER_LIST
}
public final float partialTicks;
public final ScaledResolution resolution;
public final int mouseX;
public final int mouseY;
public final ElementType type;
public RenderGameOverlayEvent(float partialTicks, ScaledResolution resolution, int mouseX, int mouseY)
{
this.partialTicks = partialTicks;
this.resolution = resolution;
this.mouseX = mouseX;
this.mouseY = mouseY;
this.type = null;
}
private RenderGameOverlayEvent(RenderGameOverlayEvent parent, ElementType type)
{
this.partialTicks = parent.partialTicks;
this.resolution = parent.resolution;
this.mouseX = parent.mouseX;
this.mouseY = parent.mouseY;
this.type = type;
}
public static class Pre extends RenderGameOverlayEvent
{
public Pre(RenderGameOverlayEvent parent, ElementType type)
{
super(parent, type);
}
}
public static class Post extends RenderGameOverlayEvent
{
public Post(RenderGameOverlayEvent parent, ElementType type)
{
super(parent, type);
}
@Override public boolean isCancelable(){ return false; }
}
public static class Text extends Pre
{
public final ArrayList<String> left;
public final ArrayList<String> right;
public Text(RenderGameOverlayEvent parent, ArrayList<String> left, ArrayList<String> right)
{
super(parent, ElementType.TEXT);
this.left = left;
this.right = right;
}
}
public static class Chat extends Pre
{
public int posX;
public int posY;
public Chat(RenderGameOverlayEvent parent, int posX, int posY)
{
super(parent, ElementType.CHAT);
this.posX = posX;
this.posY = posY;
}
}
}
@@ -0,0 +1,43 @@
package net.minecraftforge.client.event;
import net.minecraft.client.renderer.entity.RendererLivingEntity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraftforge.event.Cancelable;
import net.minecraftforge.event.Event;
public abstract class RenderLivingEvent extends Event
{
public final EntityLivingBase entity;
public final RendererLivingEntity renderer;
public RenderLivingEvent(EntityLivingBase entity, RendererLivingEntity renderer)
{
this.entity = entity;
this.renderer = renderer;
}
@Cancelable
public static class Pre extends RenderLivingEvent
{
public Pre(EntityLivingBase entity, RendererLivingEntity renderer){ super(entity, renderer); }
}
public static class Post extends RenderLivingEvent
{
public Post(EntityLivingBase entity, RendererLivingEntity renderer){ super(entity, renderer); }
}
public abstract static class Specials extends RenderLivingEvent
{
public Specials(EntityLivingBase entity, RendererLivingEntity renderer){ super(entity, renderer); }
@Cancelable
public static class Pre extends Specials
{
public Pre(EntityLivingBase entity, RendererLivingEntity renderer){ super(entity, renderer); }
}
public static class Post extends Specials
{
public Post(EntityLivingBase entity, RendererLivingEntity renderer){ super(entity, renderer); }
}
}
}
@@ -0,0 +1,76 @@
package net.minecraftforge.client.event;
import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraftforge.event.Cancelable;
import net.minecraftforge.event.entity.player.PlayerEvent;
public abstract class RenderPlayerEvent extends PlayerEvent
{
public final RenderPlayer renderer;
public final float partialRenderTick;
public RenderPlayerEvent(EntityPlayer player, RenderPlayer renderer, float partialRenderTick)
{
super(player);
this.renderer = renderer;
this.partialRenderTick = partialRenderTick;
}
@Cancelable
public static class Pre extends RenderPlayerEvent
{
public Pre(EntityPlayer player, RenderPlayer renderer, float tick){ super(player, renderer, tick); }
}
public static class Post extends RenderPlayerEvent
{
public Post(EntityPlayer player, RenderPlayer renderer, float tick){ super(player, renderer, tick); }
}
public abstract static class Specials extends RenderPlayerEvent
{
@Deprecated
public final float partialTicks;
public Specials(EntityPlayer player, RenderPlayer renderer, float partialTicks)
{
super(player, renderer, partialTicks);
this.partialTicks = partialTicks;
}
@Cancelable
public static class Pre extends Specials
{
public boolean renderHelmet = true;
public boolean renderCape = true;
public boolean renderItem = true;
public Pre(EntityPlayer player, RenderPlayer renderer, float partialTicks){ super(player, renderer, partialTicks); }
}
public static class Post extends Specials
{
public Post(EntityPlayer player, RenderPlayer renderer, float partialTicks){ super(player, renderer, partialTicks); }
}
}
public static class SetArmorModel extends RenderPlayerEvent
{
/**
* Setting this to any value besides -1 will result in the function being
* Immediately exited with the return value specified.
*/
public int result = -1;
public final int slot;
@Deprecated
public final float partialTick;
public final ItemStack stack;
public SetArmorModel(EntityPlayer player, RenderPlayer renderer, int slot, float partialTick, ItemStack stack)
{
super(player, renderer, partialTick);
this.slot = slot;
this.partialTick = partialTick;
this.stack = stack;
}
}
}
@@ -0,0 +1,15 @@
package net.minecraftforge.client.event;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraftforge.event.Event;
public class RenderWorldLastEvent extends Event
{
public final RenderGlobal context;
public final float partialTicks;
public RenderWorldLastEvent(RenderGlobal context, float partialTicks)
{
this.context = context;
this.partialTicks = partialTicks;
}
}
@@ -0,0 +1,35 @@
package net.minecraftforge.client.event;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraftforge.event.Event;
public class TextureStitchEvent extends Event
{
public final TextureMap map;
public TextureStitchEvent(TextureMap map)
{
this.map = map;
}
/**
* Fired when the TextureMap is told to refresh it's stitched texture.
* Called after the Stitched list is cleared, but before any blocks or items
* add themselves to the list.
*/
public static class Pre extends TextureStitchEvent
{
public Pre(TextureMap map){ super(map); }
}
/**
* This event is fired once the texture map has loaded all textures and
* stitched them together. All Icons should have there locations defined
* by the time this is fired.
*/
public static class Post extends TextureStitchEvent
{
public Post(TextureMap map){ super(map); }
}
}
@@ -0,0 +1,15 @@
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
import net.minecraft.client.audio.SoundPoolEntry;
/**
* Raised when the SoundManager tries to play a Background Music file,
* If you return null from this function it will prevent the sound from being played,
* you can return a different entry if you want to change the sound being played.
*/
public class PlayBackgroundMusicEvent extends SoundResultEvent
{
public PlayBackgroundMusicEvent(SoundManager manager, SoundPoolEntry entry)
{ super(manager, entry, null, 0.0f, 0.0f); }
}
@@ -0,0 +1,10 @@
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
import net.minecraft.client.audio.SoundPoolEntry;
public class PlaySoundEffectEvent extends SoundResultEvent
{
public PlaySoundEffectEvent(SoundManager manager, SoundPoolEntry source, String name, float volume, float pitch)
{ super(manager, source, name, volume, pitch); }
}
@@ -0,0 +1,14 @@
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
public class PlaySoundEffectSourceEvent extends SoundEvent
{
public final SoundManager manager;
public final String name;
public PlaySoundEffectSourceEvent(SoundManager manager, String name)
{
this.manager = manager;
this.name = name;
}
}
@@ -0,0 +1,25 @@
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
import net.minecraft.client.audio.SoundPoolEntry;
/***
* Raised when the SoundManager tries to play a normal sound,
* dogs barking, footsteps, etc. THe majority of all sounds during normal game play.
*
* If you return null from this function it will prevent the sound from being played,
* you can return a different entry if you want to change the sound being played.
*/
public class PlaySoundEvent extends SoundResultEvent
{
public final float x;
public final float y;
public final float z;
public PlaySoundEvent(SoundManager manager, SoundPoolEntry source, String name, float x, float y, float z, float volume, float pitch)
{
super(manager, source, name, volume, pitch);
this.x = x;
this.y = y;
this.z = z;
}
}
@@ -0,0 +1,20 @@
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
public class PlaySoundSourceEvent extends SoundEvent
{
public final SoundManager manager;
public final String name;
public final float x;
public final float y;
public final float z;
public PlaySoundSourceEvent(SoundManager manager, String name, float x, float y, float z)
{
this.manager = manager;
this.name = name;
this.x = x;
this.y = y;
this.z = z;
}
}
@@ -0,0 +1,26 @@
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
import net.minecraft.client.audio.SoundPoolEntry;
/**
* Raised when the SoundManager tries to play a 'Streaming' file,
* in vanilla it is only the Jukebox that uses this function.
*
* If you return null from this function it will prevent the sound from being played,
* you can return a different entry if you want to change the sound being played.
*
*/
public class PlayStreamingEvent extends SoundResultEvent
{
public final float x;
public final float y;
public final float z;
public PlayStreamingEvent(SoundManager manager, SoundPoolEntry source, String name, float x, float y, float z)
{
super(manager, source, name, 0.0f, 0.0f);
this.x = x;
this.y = y;
this.z = z;
}
}
@@ -0,0 +1,20 @@
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
public class PlayStreamingSourceEvent extends SoundEvent
{
public final SoundManager manager;
public final String name;
public final float x;
public final float y;
public final float z;
public PlayStreamingSourceEvent(SoundManager manager, String name, float x, float y, float z)
{
this.manager = manager;
this.name = name;
this.x = x;
this.y = y;
this.z = z;
}
}
@@ -0,0 +1,19 @@
package net.minecraftforge.client.event.sound;
import net.minecraft.entity.Entity;
import net.minecraft.client.audio.SoundManager;
import net.minecraft.client.audio.SoundPoolEntry;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.Cancelable;
import net.minecraftforge.event.Event;
public class SoundEvent extends Event
{
public SoundEvent(){}
public static SoundPoolEntry getResult(SoundResultEvent event)
{
MinecraftForge.EVENT_BUS.post(event);
return event.result;
}
}
@@ -0,0 +1,16 @@
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
/**
* Raised by the SoundManager.loadSoundSettings, this would be a good place for
* adding your custom sounds to the SoundPool.
*/
public class SoundLoadEvent extends SoundEvent
{
public final SoundManager manager;
public SoundLoadEvent(SoundManager manager)
{
this.manager = manager;
}
}
@@ -0,0 +1,24 @@
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
import net.minecraft.client.audio.SoundPoolEntry;
public abstract class SoundResultEvent extends SoundEvent
{
public final SoundManager manager;
public final SoundPoolEntry source;
public final String name;
public final float volume;
public final float pitch;
public SoundPoolEntry result;
public SoundResultEvent(SoundManager manager, SoundPoolEntry source, String name, float volume, float pitch)
{
this.manager = manager;
this.source = source;
this.name = name;
this.volume = volume;
this.pitch = pitch;
this.result = source;
}
}
@@ -0,0 +1,16 @@
package net.minecraftforge.client.event.sound;
import net.minecraft.client.audio.SoundManager;
/**
* This event is raised by the SoundManager when it does its first setup of the
* SoundSystemConfig's codecs, use this function to add your own codecs.
*/
public class SoundSetupEvent extends SoundEvent
{
public final SoundManager manager;
public SoundSetupEvent(SoundManager manager)
{
this.manager = manager;
}
}
@@ -0,0 +1,84 @@
package net.minecraftforge.client.model;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Map;
import net.minecraftforge.client.model.obj.ObjModelLoader;
import net.minecraftforge.client.model.techne.TechneModelLoader;
import com.google.common.collect.Maps;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/**
* Common interface for advanced model loading from files, based on file suffix
* Model support can be queried through the {@link #getSupportedSuffixes()} method.
* Instances can be created by calling {@link #loadModel(String)} with a class-loadable-path
*
* @author cpw
*
*/
@SideOnly(Side.CLIENT)
public class AdvancedModelLoader {
private static Map<String, IModelCustomLoader> instances = Maps.newHashMap();
/**
* Register a new model handler
* @param modelHandler The model handler to register
*/
public static void registerModelHandler(IModelCustomLoader modelHandler)
{
for (String suffix : modelHandler.getSuffixes())
{
instances.put(suffix, modelHandler);
}
}
/**
* Load the model from the supplied classpath resolvable resource name
* @param resourceName The resource name
* @return A model
* @throws IllegalArgumentException if the resource name cannot be understood
* @throws ModelFormatException if the underlying model handler cannot parse the model format
*/
public static IModelCustom loadModel(String resourceName) throws IllegalArgumentException, ModelFormatException
{
int i = resourceName.lastIndexOf('.');
if (i == -1)
{
FMLLog.severe("The resource name %s is not valid", resourceName);
throw new IllegalArgumentException("The resource name is not valid");
}
String suffix = resourceName.substring(i+1);
IModelCustomLoader loader = instances.get(suffix);
if (loader == null)
{
FMLLog.severe("The resource name %s is not supported", resourceName);
throw new IllegalArgumentException("The resource name is not supported");
}
URL resource = AdvancedModelLoader.class.getResource(resourceName);
if (resource == null)
{
FMLLog.severe("The resource name %s could not be found", resourceName);
throw new IllegalArgumentException("The resource name could not be found");
}
return loader.loadInstance(resourceName, resource);
}
public static Collection<String> getSupportedSuffixes()
{
return instances.keySet();
}
static
{
registerModelHandler(new ObjModelLoader());
registerModelHandler(new TechneModelLoader());
}
}
@@ -0,0 +1,11 @@
package net.minecraftforge.client.model;
public interface IModelCustom {
String getType();
void renderAll();
void renderOnly(String... groupNames);
void renderPart(String partName);
void renderAllExcept(String... excludedGroupNames);
}
@@ -0,0 +1,31 @@
package net.minecraftforge.client.model;
import java.io.InputStream;
import java.net.URL;
/**
* Instances of this class act as factories for their model type
*
* @author cpw
*
*/
public interface IModelCustomLoader {
/**
* Get the main type name for this loader
* @return the type name
*/
String getType();
/**
* Get resource suffixes this model loader recognizes
* @return a list of suffixes
*/
String[] getSuffixes();
/**
* Load a model instance from the supplied path
* @param resourceName The resource name to load
* @param resource The URL associated with the classloader resource
* @return A model instance
* @throws ModelFormatException if the model format is not correct
*/
IModelCustom loadInstance(String resourceName, URL resource) throws ModelFormatException;
}
@@ -0,0 +1,31 @@
package net.minecraftforge.client.model;
/**
* Thrown if there is a problem parsing the model
*
* @author cpw
*
*/
public class ModelFormatException extends RuntimeException {
public ModelFormatException()
{
super();
}
public ModelFormatException(String message, Throwable cause)
{
super(message, cause);
}
public ModelFormatException(String message)
{
super(message);
}
public ModelFormatException(Throwable cause)
{
super(cause);
}
}
@@ -0,0 +1,84 @@
package net.minecraftforge.client.model.obj;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.Vec3;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class Face
{
public Vertex[] vertices;
public Vertex[] vertexNormals;
public Vertex faceNormal;
public TextureCoordinate[] textureCoordinates;
public void addFaceForRender(Tessellator tessellator)
{
addFaceForRender(tessellator, 0.0005F);
}
public void addFaceForRender(Tessellator tessellator, float textureOffset)
{
if (faceNormal == null)
{
faceNormal = this.calculateFaceNormal();
}
tessellator.setNormal(faceNormal.x, faceNormal.y, faceNormal.z);
float averageU = 0F;
float averageV = 0F;
if ((textureCoordinates != null) && (textureCoordinates.length > 0))
{
for (int i = 0; i < textureCoordinates.length; ++i)
{
averageU += textureCoordinates[i].u;
averageV += textureCoordinates[i].v;
}
averageU = averageU / textureCoordinates.length;
averageV = averageV / textureCoordinates.length;
}
float offsetU, offsetV;
for (int i = 0; i < vertices.length; ++i)
{
if ((textureCoordinates != null) && (textureCoordinates.length > 0))
{
offsetU = textureOffset;
offsetV = textureOffset;
if (textureCoordinates[i].u > averageU)
{
offsetU = -offsetU;
}
if (textureCoordinates[i].v > averageV)
{
offsetV = -offsetV;
}
tessellator.addVertexWithUV(vertices[i].x, vertices[i].y, vertices[i].z, textureCoordinates[i].u + offsetU, textureCoordinates[i].v + offsetV);
}
else
{
tessellator.addVertex(vertices[i].x, vertices[i].y, vertices[i].z);
}
}
}
public Vertex calculateFaceNormal()
{
Vec3 v1 = Vec3.createVectorHelper(vertices[1].x - vertices[0].x, vertices[1].y - vertices[0].y, vertices[1].z - vertices[0].z);
Vec3 v2 = Vec3.createVectorHelper(vertices[2].x - vertices[0].x, vertices[2].y - vertices[0].y, vertices[2].z - vertices[0].z);
Vec3 normalVector = null;
normalVector = v1.crossProduct(v2).normalize();
return new Vertex((float) normalVector.xCoord, (float) normalVector.yCoord, (float) normalVector.zCoord);
}
}
@@ -0,0 +1,54 @@
package net.minecraftforge.client.model.obj;
import java.util.ArrayList;
import net.minecraft.client.renderer.Tessellator;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class GroupObject
{
public String name;
public ArrayList<Face> faces = new ArrayList<Face>();
public int glDrawingMode;
public GroupObject()
{
this("");
}
public GroupObject(String name)
{
this(name, -1);
}
public GroupObject(String name, int glDrawingMode)
{
this.name = name;
this.glDrawingMode = glDrawingMode;
}
public void render()
{
if (faces.size() > 0)
{
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawing(glDrawingMode);
render(tessellator);
tessellator.draw();
}
}
public void render(Tessellator tessellator)
{
if (faces.size() > 0)
{
for (Face face : faces)
{
face.addFaceForRender(tessellator);
}
}
}
}
@@ -0,0 +1,31 @@
package net.minecraftforge.client.model.obj;
import java.io.InputStream;
import java.net.URL;
import net.minecraftforge.client.model.IModelCustom;
import net.minecraftforge.client.model.IModelCustomLoader;
import net.minecraftforge.client.model.ModelFormatException;
public class ObjModelLoader implements IModelCustomLoader {
@Override
public String getType()
{
return "OBJ model";
}
private static final String[] types = { "obj" };
@Override
public String[] getSuffixes()
{
return types;
}
@Override
public IModelCustom loadInstance(String resourceName, URL resource) throws ModelFormatException
{
return new WavefrontObject(resourceName, resource);
}
}
@@ -0,0 +1,23 @@
package net.minecraftforge.client.model.obj;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class TextureCoordinate
{
public float u, v, w;
public TextureCoordinate(float u, float v)
{
this(u, v, 0F);
}
public TextureCoordinate(float u, float v, float w)
{
this.u = u;
this.v = v;
this.w = w;
}
}
@@ -0,0 +1,23 @@
package net.minecraftforge.client.model.obj;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class Vertex
{
public float x, y, z;
public Vertex(float x, float y)
{
this(x, y, 0F);
}
public Vertex(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
Loaded 100 of 3255 files, more files were not shown because too many files have changed in this diff. Show more