mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 17:14:44 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f7ad51a02 |
No files matched your search
+2
-18
@@ -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
|
||||
@@ -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 -->
|
||||
@@ -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:**
|
||||
@@ -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.
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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
@@ -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/
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "fml"]
|
||||
path = fml
|
||||
url = https://github.com/MinecraftForge/FML.git
|
||||
Vendored
-104
@@ -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>
|
||||
Vendored
-208
@@ -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
@@ -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.
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
Copyright (c) Forge Development LLC and contributors
|
||||
SPDX-License-Identifier: LGPL-2.1-only
|
||||
-520
@@ -1,520 +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.
|
||||
=========================================================================
|
||||
|
||||
|
||||
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
|
||||
@@ -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()
|
||||
-1820
File diff suppressed because it is too large.
Load diff
@@ -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()
|
||||
@@ -1,3 +0,0 @@
|
||||
/.gradle/
|
||||
/build/
|
||||
/out/
|
||||
@@ -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()
|
||||
}
|
||||
-32
@@ -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,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())
|
||||
}
|
||||
}
|
||||
-57
@@ -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 List<String> data = []
|
||||
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.CHECK)
|
||||
castedTask.group = 'checks'
|
||||
}
|
||||
tasks.named('checkAllAndFix').configure { it.dependsOn("checkAndFix$taskName") }
|
||||
}
|
||||
}
|
||||
+113
@@ -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 |
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ) + ", " );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,650 @@
|
||||
package net.minecraftforge.client.model.obj;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraftforge.client.model.IModelCustom;
|
||||
import net.minecraftforge.client.model.ModelFormatException;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
/**
|
||||
* Wavefront Object importer
|
||||
* Based heavily off of the specifications found at http://en.wikipedia.org/wiki/Wavefront_.obj_file
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class WavefrontObject implements IModelCustom
|
||||
{
|
||||
|
||||
private static Pattern vertexPattern = Pattern.compile("(v( (\\-){0,1}\\d+\\.\\d+){3,4} *\\n)|(v( (\\-){0,1}\\d+\\.\\d+){3,4} *$)");
|
||||
private static Pattern vertexNormalPattern = Pattern.compile("(vn( (\\-){0,1}\\d+\\.\\d+){3,4} *\\n)|(vn( (\\-){0,1}\\d+\\.\\d+){3,4} *$)");
|
||||
private static Pattern textureCoordinatePattern = Pattern.compile("(vt( (\\-){0,1}\\d+\\.\\d+){2,3} *\\n)|(vt( (\\-){0,1}\\d+\\.\\d+){2,3} *$)");
|
||||
private static Pattern face_V_VT_VN_Pattern = Pattern.compile("(f( \\d+/\\d+/\\d+){3,4} *\\n)|(f( \\d+/\\d+/\\d+){3,4} *$)");
|
||||
private static Pattern face_V_VT_Pattern = Pattern.compile("(f( \\d+/\\d+){3,4} *\\n)|(f( \\d+/\\d+){3,4} *$)");
|
||||
private static Pattern face_V_VN_Pattern = Pattern.compile("(f( \\d+//\\d+){3,4} *\\n)|(f( \\d+//\\d+){3,4} *$)");
|
||||
private static Pattern face_V_Pattern = Pattern.compile("(f( \\d+){3,4} *\\n)|(f( \\d+){3,4} *$)");
|
||||
private static Pattern groupObjectPattern = Pattern.compile("([go]( [\\w\\d]+) *\\n)|([go]( [\\w\\d]+) *$)");
|
||||
|
||||
private static Matcher vertexMatcher, vertexNormalMatcher, textureCoordinateMatcher;
|
||||
private static Matcher face_V_VT_VN_Matcher, face_V_VT_Matcher, face_V_VN_Matcher, face_V_Matcher;
|
||||
private static Matcher groupObjectMatcher;
|
||||
|
||||
public ArrayList<Vertex> vertices = new ArrayList<Vertex>();
|
||||
public ArrayList<Vertex> vertexNormals = new ArrayList<Vertex>();
|
||||
public ArrayList<TextureCoordinate> textureCoordinates = new ArrayList<TextureCoordinate>();
|
||||
public ArrayList<GroupObject> groupObjects = new ArrayList<GroupObject>();
|
||||
private GroupObject currentGroupObject;
|
||||
private String fileName;
|
||||
|
||||
public WavefrontObject(String fileName, URL resource) throws ModelFormatException
|
||||
{
|
||||
this.fileName = fileName;
|
||||
|
||||
try
|
||||
{
|
||||
loadObjModel(resource.openStream());
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ModelFormatException("IO Exception reading model format", e);
|
||||
}
|
||||
}
|
||||
|
||||
public WavefrontObject(String filename, InputStream inputStream) throws ModelFormatException
|
||||
{
|
||||
this.fileName = filename;
|
||||
loadObjModel(inputStream);
|
||||
}
|
||||
|
||||
private void loadObjModel(InputStream inputStream) throws ModelFormatException
|
||||
{
|
||||
BufferedReader reader = null;
|
||||
|
||||
String currentLine = null;
|
||||
int lineCount = 0;
|
||||
|
||||
try
|
||||
{
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
while ((currentLine = reader.readLine()) != null)
|
||||
{
|
||||
lineCount++;
|
||||
currentLine = currentLine.replaceAll("\\s+", " ").trim();
|
||||
|
||||
if (currentLine.startsWith("#") || currentLine.length() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (currentLine.startsWith("v "))
|
||||
{
|
||||
Vertex vertex = parseVertex(currentLine, lineCount);
|
||||
if (vertex != null)
|
||||
{
|
||||
vertices.add(vertex);
|
||||
}
|
||||
}
|
||||
else if (currentLine.startsWith("vn "))
|
||||
{
|
||||
Vertex vertex = parseVertexNormal(currentLine, lineCount);
|
||||
if (vertex != null)
|
||||
{
|
||||
vertexNormals.add(vertex);
|
||||
}
|
||||
}
|
||||
else if (currentLine.startsWith("vt "))
|
||||
{
|
||||
TextureCoordinate textureCoordinate = parseTextureCoordinate(currentLine, lineCount);
|
||||
if (textureCoordinate != null)
|
||||
{
|
||||
textureCoordinates.add(textureCoordinate);
|
||||
}
|
||||
}
|
||||
else if (currentLine.startsWith("f "))
|
||||
{
|
||||
|
||||
if (currentGroupObject == null)
|
||||
{
|
||||
currentGroupObject = new GroupObject("Default");
|
||||
}
|
||||
|
||||
Face face = parseFace(currentLine, lineCount);
|
||||
|
||||
if (face != null)
|
||||
{
|
||||
currentGroupObject.faces.add(face);
|
||||
}
|
||||
}
|
||||
else if (currentLine.startsWith("g ") | currentLine.startsWith("o "))
|
||||
{
|
||||
GroupObject group = parseGroupObject(currentLine, lineCount);
|
||||
|
||||
if (group != null)
|
||||
{
|
||||
if (currentGroupObject != null)
|
||||
{
|
||||
groupObjects.add(currentGroupObject);
|
||||
}
|
||||
}
|
||||
|
||||
currentGroupObject = group;
|
||||
}
|
||||
}
|
||||
|
||||
groupObjects.add(currentGroupObject);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ModelFormatException("IO Exception reading model format", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
reader.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// hush
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
inputStream.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// hush
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderAll()
|
||||
{
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
|
||||
if (currentGroupObject != null)
|
||||
{
|
||||
tessellator.startDrawing(currentGroupObject.glDrawingMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
tessellator.startDrawing(GL11.GL_TRIANGLES);
|
||||
}
|
||||
tessellateAll(tessellator);
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
public void tessellateAll(Tessellator tessellator)
|
||||
{
|
||||
for (GroupObject groupObject : groupObjects)
|
||||
{
|
||||
groupObject.render(tessellator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderOnly(String... groupNames)
|
||||
{
|
||||
for (GroupObject groupObject : groupObjects)
|
||||
{
|
||||
for (String groupName : groupNames)
|
||||
{
|
||||
if (groupName.equalsIgnoreCase(groupObject.name))
|
||||
{
|
||||
groupObject.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void tessellateOnly(Tessellator tessellator, String... groupNames) {
|
||||
for (GroupObject groupObject : groupObjects)
|
||||
{
|
||||
for (String groupName : groupNames)
|
||||
{
|
||||
if (groupName.equalsIgnoreCase(groupObject.name))
|
||||
{
|
||||
groupObject.render(tessellator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderPart(String partName)
|
||||
{
|
||||
for (GroupObject groupObject : groupObjects)
|
||||
{
|
||||
if (partName.equalsIgnoreCase(groupObject.name))
|
||||
{
|
||||
groupObject.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void tessellatePart(Tessellator tessellator, String partName) {
|
||||
for (GroupObject groupObject : groupObjects)
|
||||
{
|
||||
if (partName.equalsIgnoreCase(groupObject.name))
|
||||
{
|
||||
groupObject.render(tessellator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void renderAllExcept(String... excludedGroupNames)
|
||||
{
|
||||
for (GroupObject groupObject : groupObjects)
|
||||
{
|
||||
boolean skipPart=false;
|
||||
for (String excludedGroupName : excludedGroupNames)
|
||||
{
|
||||
if (excludedGroupName.equalsIgnoreCase(groupObject.name))
|
||||
{
|
||||
skipPart=true;
|
||||
}
|
||||
}
|
||||
if(!skipPart)
|
||||
{
|
||||
groupObject.render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void tessellateAllExcept(Tessellator tessellator, String... excludedGroupNames)
|
||||
{
|
||||
boolean exclude;
|
||||
for (GroupObject groupObject : groupObjects)
|
||||
{
|
||||
exclude=false;
|
||||
for (String excludedGroupName : excludedGroupNames)
|
||||
{
|
||||
if (excludedGroupName.equalsIgnoreCase(groupObject.name))
|
||||
{
|
||||
exclude=true;
|
||||
}
|
||||
}
|
||||
if(!exclude)
|
||||
{
|
||||
groupObject.render(tessellator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Vertex parseVertex(String line, int lineCount) throws ModelFormatException
|
||||
{
|
||||
Vertex vertex = null;
|
||||
|
||||
if (isValidVertexLine(line))
|
||||
{
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
String[] tokens = line.split(" ");
|
||||
|
||||
try
|
||||
{
|
||||
if (tokens.length == 2)
|
||||
{
|
||||
return new Vertex(Float.parseFloat(tokens[0]), Float.parseFloat(tokens[1]));
|
||||
}
|
||||
else if (tokens.length == 3)
|
||||
{
|
||||
return new Vertex(Float.parseFloat(tokens[0]), Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]));
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new ModelFormatException(String.format("Number formatting error at line %d",lineCount), e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
|
||||
return vertex;
|
||||
}
|
||||
|
||||
private Vertex parseVertexNormal(String line, int lineCount) throws ModelFormatException
|
||||
{
|
||||
Vertex vertexNormal = null;
|
||||
|
||||
if (isValidVertexNormalLine(line))
|
||||
{
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
String[] tokens = line.split(" ");
|
||||
|
||||
try
|
||||
{
|
||||
if (tokens.length == 3)
|
||||
return new Vertex(Float.parseFloat(tokens[0]), Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new ModelFormatException(String.format("Number formatting error at line %d",lineCount), e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
|
||||
return vertexNormal;
|
||||
}
|
||||
|
||||
private TextureCoordinate parseTextureCoordinate(String line, int lineCount) throws ModelFormatException
|
||||
{
|
||||
TextureCoordinate textureCoordinate = null;
|
||||
|
||||
if (isValidTextureCoordinateLine(line))
|
||||
{
|
||||
line = line.substring(line.indexOf(" ") + 1);
|
||||
String[] tokens = line.split(" ");
|
||||
|
||||
try
|
||||
{
|
||||
if (tokens.length == 2)
|
||||
return new TextureCoordinate(Float.parseFloat(tokens[0]), 1 - Float.parseFloat(tokens[1]));
|
||||
else if (tokens.length == 3)
|
||||
return new TextureCoordinate(Float.parseFloat(tokens[0]), 1 - Float.parseFloat(tokens[1]), Float.parseFloat(tokens[2]));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
throw new ModelFormatException(String.format("Number formatting error at line %d",lineCount), e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
|
||||
return textureCoordinate;
|
||||
}
|
||||
|
||||
private Face parseFace(String line, int lineCount) throws ModelFormatException
|
||||
{
|
||||
Face face = null;
|
||||
|
||||
if (isValidFaceLine(line))
|
||||
{
|
||||
face = new Face();
|
||||
|
||||
String trimmedLine = line.substring(line.indexOf(" ") + 1);
|
||||
String[] tokens = trimmedLine.split(" ");
|
||||
String[] subTokens = null;
|
||||
|
||||
if (tokens.length == 3)
|
||||
{
|
||||
if (currentGroupObject.glDrawingMode == -1)
|
||||
{
|
||||
currentGroupObject.glDrawingMode = GL11.GL_TRIANGLES;
|
||||
}
|
||||
else if (currentGroupObject.glDrawingMode != GL11.GL_TRIANGLES)
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Invalid number of points for face (expected 4, found " + tokens.length + ")");
|
||||
}
|
||||
}
|
||||
else if (tokens.length == 4)
|
||||
{
|
||||
if (currentGroupObject.glDrawingMode == -1)
|
||||
{
|
||||
currentGroupObject.glDrawingMode = GL11.GL_QUADS;
|
||||
}
|
||||
else if (currentGroupObject.glDrawingMode != GL11.GL_QUADS)
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Invalid number of points for face (expected 3, found " + tokens.length + ")");
|
||||
}
|
||||
}
|
||||
|
||||
// f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ...
|
||||
if (isValidFace_V_VT_VN_Line(line))
|
||||
{
|
||||
face.vertices = new Vertex[tokens.length];
|
||||
face.textureCoordinates = new TextureCoordinate[tokens.length];
|
||||
face.vertexNormals = new Vertex[tokens.length];
|
||||
|
||||
for (int i = 0; i < tokens.length; ++i)
|
||||
{
|
||||
subTokens = tokens[i].split("/");
|
||||
|
||||
face.vertices[i] = vertices.get(Integer.parseInt(subTokens[0]) - 1);
|
||||
face.textureCoordinates[i] = textureCoordinates.get(Integer.parseInt(subTokens[1]) - 1);
|
||||
face.vertexNormals[i] = vertexNormals.get(Integer.parseInt(subTokens[2]) - 1);
|
||||
}
|
||||
|
||||
face.faceNormal = face.calculateFaceNormal();
|
||||
}
|
||||
// f v1/vt1 v2/vt2 v3/vt3 ...
|
||||
else if (isValidFace_V_VT_Line(line))
|
||||
{
|
||||
face.vertices = new Vertex[tokens.length];
|
||||
face.textureCoordinates = new TextureCoordinate[tokens.length];
|
||||
|
||||
for (int i = 0; i < tokens.length; ++i)
|
||||
{
|
||||
subTokens = tokens[i].split("/");
|
||||
|
||||
face.vertices[i] = vertices.get(Integer.parseInt(subTokens[0]) - 1);
|
||||
face.textureCoordinates[i] = textureCoordinates.get(Integer.parseInt(subTokens[1]) - 1);
|
||||
}
|
||||
|
||||
face.faceNormal = face.calculateFaceNormal();
|
||||
}
|
||||
// f v1//vn1 v2//vn2 v3//vn3 ...
|
||||
else if (isValidFace_V_VN_Line(line))
|
||||
{
|
||||
face.vertices = new Vertex[tokens.length];
|
||||
face.vertexNormals = new Vertex[tokens.length];
|
||||
|
||||
for (int i = 0; i < tokens.length; ++i)
|
||||
{
|
||||
subTokens = tokens[i].split("//");
|
||||
|
||||
face.vertices[i] = vertices.get(Integer.parseInt(subTokens[0]) - 1);
|
||||
face.vertexNormals[i] = vertexNormals.get(Integer.parseInt(subTokens[1]) - 1);
|
||||
}
|
||||
|
||||
face.faceNormal = face.calculateFaceNormal();
|
||||
}
|
||||
// f v1 v2 v3 ...
|
||||
else if (isValidFace_V_Line(line))
|
||||
{
|
||||
face.vertices = new Vertex[tokens.length];
|
||||
|
||||
for (int i = 0; i < tokens.length; ++i)
|
||||
{
|
||||
face.vertices[i] = vertices.get(Integer.parseInt(tokens[i]) - 1);
|
||||
}
|
||||
|
||||
face.faceNormal = face.calculateFaceNormal();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
|
||||
return face;
|
||||
}
|
||||
|
||||
private GroupObject parseGroupObject(String line, int lineCount) throws ModelFormatException
|
||||
{
|
||||
GroupObject group = null;
|
||||
|
||||
if (isValidGroupObjectLine(line))
|
||||
{
|
||||
String trimmedLine = line.substring(line.indexOf(" ") + 1);
|
||||
|
||||
if (trimmedLine.length() > 0)
|
||||
{
|
||||
group = new GroupObject(trimmedLine);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ModelFormatException("Error parsing entry ('" + line + "'" + ", line " + lineCount + ") in file '" + fileName + "' - Incorrect format");
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid vertex
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid vertex, false otherwise
|
||||
*/
|
||||
private static boolean isValidVertexLine(String line)
|
||||
{
|
||||
if (vertexMatcher != null)
|
||||
{
|
||||
vertexMatcher.reset();
|
||||
}
|
||||
|
||||
vertexMatcher = vertexPattern.matcher(line);
|
||||
return vertexMatcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid vertex normal
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid vertex normal, false otherwise
|
||||
*/
|
||||
private static boolean isValidVertexNormalLine(String line)
|
||||
{
|
||||
if (vertexNormalMatcher != null)
|
||||
{
|
||||
vertexNormalMatcher.reset();
|
||||
}
|
||||
|
||||
vertexNormalMatcher = vertexNormalPattern.matcher(line);
|
||||
return vertexNormalMatcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid texture coordinate
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid texture coordinate, false otherwise
|
||||
*/
|
||||
private static boolean isValidTextureCoordinateLine(String line)
|
||||
{
|
||||
if (textureCoordinateMatcher != null)
|
||||
{
|
||||
textureCoordinateMatcher.reset();
|
||||
}
|
||||
|
||||
textureCoordinateMatcher = textureCoordinatePattern.matcher(line);
|
||||
return textureCoordinateMatcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid face that is described by vertices, texture coordinates, and vertex normals
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid face that matches the format "f v1/vt1/vn1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise
|
||||
*/
|
||||
private static boolean isValidFace_V_VT_VN_Line(String line)
|
||||
{
|
||||
if (face_V_VT_VN_Matcher != null)
|
||||
{
|
||||
face_V_VT_VN_Matcher.reset();
|
||||
}
|
||||
|
||||
face_V_VT_VN_Matcher = face_V_VT_VN_Pattern.matcher(line);
|
||||
return face_V_VT_VN_Matcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid face that is described by vertices and texture coordinates
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid face that matches the format "f v1/vt1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise
|
||||
*/
|
||||
private static boolean isValidFace_V_VT_Line(String line)
|
||||
{
|
||||
if (face_V_VT_Matcher != null)
|
||||
{
|
||||
face_V_VT_Matcher.reset();
|
||||
}
|
||||
|
||||
face_V_VT_Matcher = face_V_VT_Pattern.matcher(line);
|
||||
return face_V_VT_Matcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid face that is described by vertices and vertex normals
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid face that matches the format "f v1//vn1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise
|
||||
*/
|
||||
private static boolean isValidFace_V_VN_Line(String line)
|
||||
{
|
||||
if (face_V_VN_Matcher != null)
|
||||
{
|
||||
face_V_VN_Matcher.reset();
|
||||
}
|
||||
|
||||
face_V_VN_Matcher = face_V_VN_Pattern.matcher(line);
|
||||
return face_V_VN_Matcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid face that is described by only vertices
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid face that matches the format "f v1 ..." (with a minimum of 3 points in the face, and a maximum of 4), false otherwise
|
||||
*/
|
||||
private static boolean isValidFace_V_Line(String line)
|
||||
{
|
||||
if (face_V_Matcher != null)
|
||||
{
|
||||
face_V_Matcher.reset();
|
||||
}
|
||||
|
||||
face_V_Matcher = face_V_Pattern.matcher(line);
|
||||
return face_V_Matcher.matches();
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid face of any of the possible face formats
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid face that matches any of the valid face formats, false otherwise
|
||||
*/
|
||||
private static boolean isValidFaceLine(String line)
|
||||
{
|
||||
return isValidFace_V_VT_VN_Line(line) || isValidFace_V_VT_Line(line) || isValidFace_V_VN_Line(line) || isValidFace_V_Line(line);
|
||||
}
|
||||
|
||||
/***
|
||||
* Verifies that the given line from the model file is a valid group (or object)
|
||||
* @param line the line being validated
|
||||
* @return true if the line is a valid group (or object), false otherwise
|
||||
*/
|
||||
private static boolean isValidGroupObjectLine(String line)
|
||||
{
|
||||
if (groupObjectMatcher != null)
|
||||
{
|
||||
groupObjectMatcher.reset();
|
||||
}
|
||||
|
||||
groupObjectMatcher = groupObjectPattern.matcher(line);
|
||||
return groupObjectMatcher.matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType()
|
||||
{
|
||||
return "obj";
|
||||
}
|
||||
}
|
||||
Loaded 100 of 3180 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user