Init
15
Tools/Platform/Android/AndroidProject/.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
3
Tools/Platform/Android/AndroidProject/.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
1
Tools/Platform/Android/AndroidProject/.idea/.name
generated
Normal file
@ -0,0 +1 @@
|
||||
IAEGame
|
||||
6
Tools/Platform/Android/AndroidProject/.idea/AndroidProjectSystem.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidProjectSystem">
|
||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||
</component>
|
||||
</project>
|
||||
6
Tools/Platform/Android/AndroidProject/.idea/compiler.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
||||
10
Tools/Platform/Android/AndroidProject/.idea/deploymentTargetSelector.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
19
Tools/Platform/Android/AndroidProject/.idea/gradle.xml
generated
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
61
Tools/Platform/Android/AndroidProject/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@ -0,0 +1,61 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<option name="composableFile" value="true" />
|
||||
<option name="previewFile" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
10
Tools/Platform/Android/AndroidProject/.idea/migrations.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
10
Tools/Platform/Android/AndroidProject/.idea/misc.xml
generated
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
17
Tools/Platform/Android/AndroidProject/.idea/runConfigurations.xml
generated
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
14
Tools/Platform/Android/AndroidProject/.idea/vcs.xml
generated
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/IACore" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/RmlUi" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/SDL" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/SDL_mixer" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/freetype" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/json" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/nativefiledialog" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../Vendor/zlib" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
1
Tools/Platform/Android/AndroidProject/app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
||||
59
Tools/Platform/Android/AndroidProject/app/build.gradle.kts
Normal file
@ -0,0 +1,59 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.iasoft.iaegame"
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.iasoft.iaegame"
|
||||
minSdk = 30
|
||||
targetSdk = 36
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.compose.ui)
|
||||
implementation(libs.androidx.compose.ui.graphics)
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
}
|
||||
21
Tools/Platform/Android/AndroidProject/app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- OpenGL ES 2.0 -->
|
||||
<uses-feature android:glEsVersion="0x00020000" />
|
||||
|
||||
<!-- Touchscreen support -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
android:required="false" />
|
||||
|
||||
<!-- Game controller support -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.bluetooth"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.gamepad"
|
||||
android:required="false" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.usb.host"
|
||||
android:required="false" />
|
||||
|
||||
<!-- External mouse input events -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.type.pc"
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.IAEGame"
|
||||
android:hardwareAccelerated="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.IAEGame"
|
||||
android:configChanges="layoutDirection|locale|grammaticalGender|fontScale|fontWeightAdjustment|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||
android:preferMinimalPostProcessing="true"
|
||||
>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
After Width: | Height: | Size: 78 KiB |
@ -0,0 +1,16 @@
|
||||
package com.iasoft.iaegame
|
||||
|
||||
import android.os.Bundle
|
||||
import org.libsdl.app.SDLActivity
|
||||
|
||||
class MainActivity : SDLActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun getLibraries(): Array<out String?>? {
|
||||
return arrayOf<String>(
|
||||
"Game"
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
package com.iasoft.iaegame.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
@ -0,0 +1,58 @@
|
||||
package com.iasoft.iaegame.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun IAEGameTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.iasoft.iaegame.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
||||
@ -0,0 +1,21 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.hardware.usb.UsbDevice;
|
||||
|
||||
interface HIDDevice
|
||||
{
|
||||
public int getId();
|
||||
public int getVendorId();
|
||||
public int getProductId();
|
||||
public String getSerialNumber();
|
||||
public int getVersion();
|
||||
public String getManufacturerName();
|
||||
public String getProductName();
|
||||
public UsbDevice getDevice();
|
||||
public boolean open();
|
||||
public int writeReport(byte[] report, boolean feature);
|
||||
public boolean readReport(byte[] report, boolean feature);
|
||||
public void setFrozen(boolean frozen);
|
||||
public void close();
|
||||
public void shutdown();
|
||||
}
|
||||
@ -0,0 +1,655 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCallback;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.bluetooth.BluetoothGattDescriptor;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.bluetooth.BluetoothGattService;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.os.*;
|
||||
|
||||
//import com.android.internal.util.HexDump;
|
||||
|
||||
import java.lang.Runnable;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.UUID;
|
||||
|
||||
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
|
||||
|
||||
private static final String TAG = "hidapi";
|
||||
private HIDDeviceManager mManager;
|
||||
private BluetoothDevice mDevice;
|
||||
private int mDeviceId;
|
||||
private BluetoothGatt mGatt;
|
||||
private boolean mIsRegistered = false;
|
||||
private boolean mIsConnected = false;
|
||||
private boolean mIsChromebook = false;
|
||||
private boolean mIsReconnecting = false;
|
||||
private boolean mFrozen = false;
|
||||
private LinkedList<GattOperation> mOperations;
|
||||
GattOperation mCurrentOperation = null;
|
||||
private Handler mHandler;
|
||||
|
||||
private static final int TRANSPORT_AUTO = 0;
|
||||
private static final int TRANSPORT_BREDR = 1;
|
||||
private static final int TRANSPORT_LE = 2;
|
||||
|
||||
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
|
||||
|
||||
static final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
|
||||
static final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
|
||||
static final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
|
||||
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
|
||||
|
||||
static class GattOperation {
|
||||
private enum Operation {
|
||||
CHR_READ,
|
||||
CHR_WRITE,
|
||||
ENABLE_NOTIFICATION
|
||||
}
|
||||
|
||||
Operation mOp;
|
||||
UUID mUuid;
|
||||
byte[] mValue;
|
||||
BluetoothGatt mGatt;
|
||||
boolean mResult = true;
|
||||
|
||||
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
|
||||
mGatt = gatt;
|
||||
mOp = operation;
|
||||
mUuid = uuid;
|
||||
}
|
||||
|
||||
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
|
||||
mGatt = gatt;
|
||||
mOp = operation;
|
||||
mUuid = uuid;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// This is executed in main thread
|
||||
BluetoothGattCharacteristic chr;
|
||||
|
||||
switch (mOp) {
|
||||
case CHR_READ:
|
||||
chr = getCharacteristic(mUuid);
|
||||
//Log.v(TAG, "Reading characteristic " + chr.getUuid());
|
||||
if (!mGatt.readCharacteristic(chr)) {
|
||||
Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
|
||||
mResult = false;
|
||||
break;
|
||||
}
|
||||
mResult = true;
|
||||
break;
|
||||
case CHR_WRITE:
|
||||
chr = getCharacteristic(mUuid);
|
||||
//Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
|
||||
chr.setValue(mValue);
|
||||
if (!mGatt.writeCharacteristic(chr)) {
|
||||
Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
|
||||
mResult = false;
|
||||
break;
|
||||
}
|
||||
mResult = true;
|
||||
break;
|
||||
case ENABLE_NOTIFICATION:
|
||||
chr = getCharacteristic(mUuid);
|
||||
//Log.v(TAG, "Writing descriptor of " + chr.getUuid());
|
||||
if (chr != null) {
|
||||
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||
if (cccd != null) {
|
||||
int properties = chr.getProperties();
|
||||
byte[] value;
|
||||
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
|
||||
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
|
||||
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
|
||||
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
|
||||
} else {
|
||||
Log.e(TAG, "Unable to start notifications on input characteristic");
|
||||
mResult = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mGatt.setCharacteristicNotification(chr, true);
|
||||
cccd.setValue(value);
|
||||
if (!mGatt.writeDescriptor(cccd)) {
|
||||
Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
|
||||
mResult = false;
|
||||
return;
|
||||
}
|
||||
mResult = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean finish() {
|
||||
return mResult;
|
||||
}
|
||||
|
||||
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
|
||||
BluetoothGattService valveService = mGatt.getService(steamControllerService);
|
||||
if (valveService == null)
|
||||
return null;
|
||||
return valveService.getCharacteristic(uuid);
|
||||
}
|
||||
|
||||
static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
|
||||
return new GattOperation(gatt, Operation.CHR_READ, uuid);
|
||||
}
|
||||
|
||||
static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
|
||||
return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
|
||||
}
|
||||
|
||||
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
|
||||
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
|
||||
}
|
||||
}
|
||||
|
||||
HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
|
||||
mManager = manager;
|
||||
mDevice = device;
|
||||
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
|
||||
mIsRegistered = false;
|
||||
mIsChromebook = SDLActivity.isChromebook();
|
||||
mOperations = new LinkedList<GattOperation>();
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
mGatt = connectGatt();
|
||||
// final HIDDeviceBLESteamController finalThis = this;
|
||||
// mHandler.postDelayed(new Runnable() {
|
||||
// @Override
|
||||
// void run() {
|
||||
// finalThis.checkConnectionForChromebookIssue();
|
||||
// }
|
||||
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||
}
|
||||
|
||||
String getIdentifier() {
|
||||
return String.format("SteamController.%s", mDevice.getAddress());
|
||||
}
|
||||
|
||||
BluetoothGatt getGatt() {
|
||||
return mGatt;
|
||||
}
|
||||
|
||||
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
|
||||
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
|
||||
private BluetoothGatt connectGatt(boolean managed) {
|
||||
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||
try {
|
||||
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
|
||||
} catch (Exception e) {
|
||||
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||
}
|
||||
} else {
|
||||
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||
}
|
||||
}
|
||||
|
||||
private BluetoothGatt connectGatt() {
|
||||
return connectGatt(false);
|
||||
}
|
||||
|
||||
protected int getConnectionState() {
|
||||
|
||||
Context context = mManager.getContext();
|
||||
if (context == null) {
|
||||
// We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
|
||||
return BluetoothProfile.STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
if (btManager == null) {
|
||||
// This device doesn't support Bluetooth. We should never be here, because how did
|
||||
// we instantiate a device to start with?
|
||||
return BluetoothProfile.STATE_DISCONNECTED;
|
||||
}
|
||||
|
||||
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
|
||||
}
|
||||
|
||||
void reconnect() {
|
||||
|
||||
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void checkConnectionForChromebookIssue() {
|
||||
if (!mIsChromebook) {
|
||||
// We only do this on Chromebooks, because otherwise it's really annoying to just attempt
|
||||
// over and over.
|
||||
return;
|
||||
}
|
||||
|
||||
int connectionState = getConnectionState();
|
||||
|
||||
switch (connectionState) {
|
||||
case BluetoothProfile.STATE_CONNECTED:
|
||||
if (!mIsConnected) {
|
||||
// We are in the Bad Chromebook Place. We can force a disconnect
|
||||
// to try to recover.
|
||||
Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
break;
|
||||
}
|
||||
else if (!isRegistered()) {
|
||||
if (mGatt.getServices().size() > 0) {
|
||||
Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
|
||||
probeService(this);
|
||||
}
|
||||
else {
|
||||
Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case BluetoothProfile.STATE_DISCONNECTED:
|
||||
Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
|
||||
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
break;
|
||||
|
||||
case BluetoothProfile.STATE_CONNECTING:
|
||||
Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
|
||||
break;
|
||||
}
|
||||
|
||||
final HIDDeviceBLESteamController finalThis = this;
|
||||
mHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finalThis.checkConnectionForChromebookIssue();
|
||||
}
|
||||
}, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||
}
|
||||
|
||||
private boolean isRegistered() {
|
||||
return mIsRegistered;
|
||||
}
|
||||
|
||||
private void setRegistered() {
|
||||
mIsRegistered = true;
|
||||
}
|
||||
|
||||
private boolean probeService(HIDDeviceBLESteamController controller) {
|
||||
|
||||
if (isRegistered()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mIsConnected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Log.v(TAG, "probeService controller=" + controller);
|
||||
|
||||
for (BluetoothGattService service : mGatt.getServices()) {
|
||||
if (service.getUuid().equals(steamControllerService)) {
|
||||
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
|
||||
|
||||
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
|
||||
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||
Log.v(TAG, "Found input characteristic");
|
||||
// Start notifications
|
||||
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||
if (cccd != null) {
|
||||
enableNotification(chr.getUuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
|
||||
Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
|
||||
mIsConnected = false;
|
||||
mIsReconnecting = true;
|
||||
mGatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void finishCurrentGattOperation() {
|
||||
GattOperation op = null;
|
||||
synchronized (mOperations) {
|
||||
if (mCurrentOperation != null) {
|
||||
op = mCurrentOperation;
|
||||
mCurrentOperation = null;
|
||||
}
|
||||
}
|
||||
if (op != null) {
|
||||
boolean result = op.finish(); // TODO: Maybe in main thread as well?
|
||||
|
||||
// Our operation failed, let's add it back to the beginning of our queue.
|
||||
if (!result) {
|
||||
mOperations.addFirst(op);
|
||||
}
|
||||
}
|
||||
executeNextGattOperation();
|
||||
}
|
||||
|
||||
private void executeNextGattOperation() {
|
||||
synchronized (mOperations) {
|
||||
if (mCurrentOperation != null)
|
||||
return;
|
||||
|
||||
if (mOperations.isEmpty())
|
||||
return;
|
||||
|
||||
mCurrentOperation = mOperations.removeFirst();
|
||||
}
|
||||
|
||||
// Run in main thread
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (mOperations) {
|
||||
if (mCurrentOperation == null) {
|
||||
Log.e(TAG, "Current operation null in executor?");
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentOperation.run();
|
||||
// now wait for the GATT callback and when it comes, finish this operation
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void queueGattOperation(GattOperation op) {
|
||||
synchronized (mOperations) {
|
||||
mOperations.add(op);
|
||||
}
|
||||
executeNextGattOperation();
|
||||
}
|
||||
|
||||
private void enableNotification(UUID chrUuid) {
|
||||
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
|
||||
queueGattOperation(op);
|
||||
}
|
||||
|
||||
void writeCharacteristic(UUID uuid, byte[] value) {
|
||||
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
|
||||
queueGattOperation(op);
|
||||
}
|
||||
|
||||
void readCharacteristic(UUID uuid) {
|
||||
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
|
||||
queueGattOperation(op);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////// BluetoothGattCallback overridden methods
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
|
||||
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
|
||||
mIsReconnecting = false;
|
||||
if (newState == 2) {
|
||||
mIsConnected = true;
|
||||
// Run directly, without GattOperation
|
||||
if (!isRegistered()) {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mGatt.discoverServices();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (newState == 0) {
|
||||
mIsConnected = false;
|
||||
}
|
||||
|
||||
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||
//Log.v(TAG, "onServicesDiscovered status=" + status);
|
||||
if (status == 0) {
|
||||
if (gatt.getServices().size() == 0) {
|
||||
Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
|
||||
mIsReconnecting = true;
|
||||
mIsConnected = false;
|
||||
gatt.disconnect();
|
||||
mGatt = connectGatt(false);
|
||||
}
|
||||
else {
|
||||
probeService(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
|
||||
|
||||
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
|
||||
mManager.HIDDeviceReportResponse(getId(), characteristic.getValue());
|
||||
}
|
||||
|
||||
finishCurrentGattOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
|
||||
|
||||
if (characteristic.getUuid().equals(reportCharacteristic)) {
|
||||
// Only register controller with the native side once it has been fully configured
|
||||
if (!isRegistered()) {
|
||||
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
|
||||
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0, true);
|
||||
setRegistered();
|
||||
}
|
||||
}
|
||||
|
||||
finishCurrentGattOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||
// Enable this for verbose logging of controller input reports
|
||||
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
|
||||
|
||||
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
|
||||
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
//Log.v(TAG, "onDescriptorRead status=" + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
|
||||
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
|
||||
|
||||
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||
boolean hasWrittenInputDescriptor = true;
|
||||
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
|
||||
if (reportChr != null) {
|
||||
Log.v(TAG, "Writing report characteristic to enter valve mode");
|
||||
reportChr.setValue(enterValveMode);
|
||||
gatt.writeCharacteristic(reportChr);
|
||||
}
|
||||
}
|
||||
|
||||
finishCurrentGattOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
|
||||
//Log.v(TAG, "onReadRemoteRssi status=" + status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||
//Log.v(TAG, "onMtuChanged status=" + status);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////// Public API
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return mDeviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVendorId() {
|
||||
// Valve Corporation
|
||||
final int VALVE_USB_VID = 0x28DE;
|
||||
return VALVE_USB_VID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProductId() {
|
||||
// We don't have an easy way to query from the Bluetooth device, but we know what it is
|
||||
final int D0G_BLE2_PID = 0x1106;
|
||||
return D0G_BLE2_PID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerialNumber() {
|
||||
// This will be read later via feature report by Steam
|
||||
return "12345";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturerName() {
|
||||
return "Valve Corporation";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProductName() {
|
||||
return "Steam Controller";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbDevice getDevice() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeReport(byte[] report, boolean feature) {
|
||||
if (!isRegistered()) {
|
||||
Log.e(TAG, "Attempted writeReport before Steam Controller is registered!");
|
||||
if (mIsConnected) {
|
||||
probeService(this);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (feature) {
|
||||
// We need to skip the first byte, as that doesn't go over the air
|
||||
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
|
||||
//Log.v(TAG, "writeFeatureReport " + HexDump.dumpHexString(actual_report));
|
||||
writeCharacteristic(reportCharacteristic, actual_report);
|
||||
return report.length;
|
||||
} else {
|
||||
//Log.v(TAG, "writeOutputReport " + HexDump.dumpHexString(report));
|
||||
writeCharacteristic(reportCharacteristic, report);
|
||||
return report.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readReport(byte[] report, boolean feature) {
|
||||
if (!isRegistered()) {
|
||||
Log.e(TAG, "Attempted readReport before Steam Controller is registered!");
|
||||
if (mIsConnected) {
|
||||
probeService(this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (feature) {
|
||||
readCharacteristic(reportCharacteristic);
|
||||
return true;
|
||||
} else {
|
||||
// Not implemented
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrozen(boolean frozen) {
|
||||
mFrozen = frozen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
close();
|
||||
|
||||
BluetoothGatt g = mGatt;
|
||||
if (g != null) {
|
||||
g.disconnect();
|
||||
g.close();
|
||||
mGatt = null;
|
||||
}
|
||||
mManager = null;
|
||||
mIsRegistered = false;
|
||||
mIsConnected = false;
|
||||
mOperations.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,690 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.usb.*;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class HIDDeviceManager {
|
||||
private static final String TAG = "hidapi";
|
||||
private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
|
||||
|
||||
private static HIDDeviceManager sManager;
|
||||
private static int sManagerRefCount = 0;
|
||||
|
||||
static public HIDDeviceManager acquire(Context context) {
|
||||
if (sManagerRefCount == 0) {
|
||||
sManager = new HIDDeviceManager(context);
|
||||
}
|
||||
++sManagerRefCount;
|
||||
return sManager;
|
||||
}
|
||||
|
||||
static public void release(HIDDeviceManager manager) {
|
||||
if (manager == sManager) {
|
||||
--sManagerRefCount;
|
||||
if (sManagerRefCount == 0) {
|
||||
sManager.close();
|
||||
sManager = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
|
||||
private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
|
||||
private int mNextDeviceId = 0;
|
||||
private SharedPreferences mSharedPreferences = null;
|
||||
private boolean mIsChromebook = false;
|
||||
private UsbManager mUsbManager;
|
||||
private Handler mHandler;
|
||||
private BluetoothManager mBluetoothManager;
|
||||
private List<BluetoothDevice> mLastBluetoothDevices;
|
||||
|
||||
private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
|
||||
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
handleUsbDeviceAttached(usbDevice);
|
||||
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
|
||||
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
handleUsbDeviceDetached(usbDevice);
|
||||
} else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
|
||||
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
// Bluetooth device was connected. If it was a Steam Controller, handle it
|
||||
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
Log.d(TAG, "Bluetooth device connected: " + device);
|
||||
|
||||
if (isSteamController(device)) {
|
||||
connectBluetoothDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
// Bluetooth device was disconnected, remove from controller manager (if any)
|
||||
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
|
||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||
Log.d(TAG, "Bluetooth device disconnected: " + device);
|
||||
|
||||
disconnectBluetoothDevice(device);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private HIDDeviceManager(final Context context) {
|
||||
mContext = context;
|
||||
|
||||
HIDDeviceRegisterCallback();
|
||||
|
||||
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
|
||||
mIsChromebook = SDLActivity.isChromebook();
|
||||
|
||||
// if (shouldClear) {
|
||||
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||
// spedit.clear();
|
||||
// spedit.apply();
|
||||
// }
|
||||
// else
|
||||
{
|
||||
mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
|
||||
}
|
||||
}
|
||||
|
||||
Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
int getDeviceIDForIdentifier(String identifier) {
|
||||
SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||
|
||||
int result = mSharedPreferences.getInt(identifier, 0);
|
||||
if (result == 0) {
|
||||
result = mNextDeviceId++;
|
||||
spedit.putInt("next_device_id", mNextDeviceId);
|
||||
}
|
||||
|
||||
spedit.putInt(identifier, result);
|
||||
spedit.apply();
|
||||
return result;
|
||||
}
|
||||
|
||||
private void initializeUSB() {
|
||||
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
|
||||
if (mUsbManager == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
// Logging
|
||||
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
|
||||
Log.i(TAG,"Path: " + device.getDeviceName());
|
||||
Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
|
||||
Log.i(TAG,"Product: " + device.getProductName());
|
||||
Log.i(TAG,"ID: " + device.getDeviceId());
|
||||
Log.i(TAG,"Class: " + device.getDeviceClass());
|
||||
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
|
||||
Log.i(TAG,"Vendor ID " + device.getVendorId());
|
||||
Log.i(TAG,"Product ID: " + device.getProductId());
|
||||
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
|
||||
Log.i(TAG,"---------------------------------------");
|
||||
|
||||
// Get interface details
|
||||
for (int index = 0; index < device.getInterfaceCount(); index++) {
|
||||
UsbInterface mUsbInterface = device.getInterface(index);
|
||||
Log.i(TAG," ***** *****");
|
||||
Log.i(TAG," Interface index: " + index);
|
||||
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
|
||||
Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
|
||||
Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
|
||||
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
|
||||
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
|
||||
|
||||
// Get endpoint details
|
||||
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
|
||||
{
|
||||
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
|
||||
Log.i(TAG," ++++ ++++ ++++");
|
||||
Log.i(TAG," Endpoint index: " + epi);
|
||||
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
|
||||
Log.i(TAG," Direction: " + mEndpoint.getDirection());
|
||||
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
|
||||
Log.i(TAG," Interval: " + mEndpoint.getInterval());
|
||||
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
|
||||
Log.i(TAG," Type: " + mEndpoint.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.i(TAG," No more devices connected.");
|
||||
*/
|
||||
|
||||
// Register for USB broadcasts and permission completions
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
|
||||
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
|
||||
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||
if (Build.VERSION.SDK_INT >= 33) { /* Android 13.0 (TIRAMISU) */
|
||||
mContext.registerReceiver(mUsbBroadcast, filter, Context.RECEIVER_EXPORTED);
|
||||
} else {
|
||||
mContext.registerReceiver(mUsbBroadcast, filter);
|
||||
}
|
||||
|
||||
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
|
||||
handleUsbDeviceAttached(usbDevice);
|
||||
}
|
||||
}
|
||||
|
||||
UsbManager getUSBManager() {
|
||||
return mUsbManager;
|
||||
}
|
||||
|
||||
private void shutdownUSB() {
|
||||
try {
|
||||
mContext.unregisterReceiver(mUsbBroadcast);
|
||||
} catch (Exception e) {
|
||||
// We may not have registered, that's okay
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
|
||||
return true;
|
||||
}
|
||||
if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||
final int XB360_IFACE_SUBCLASS = 93;
|
||||
final int XB360_IFACE_PROTOCOL = 1; // Wired
|
||||
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
|
||||
final int[] SUPPORTED_VENDORS = {
|
||||
0x0079, // GPD Win 2
|
||||
0x044f, // Thrustmaster
|
||||
0x045e, // Microsoft
|
||||
0x046d, // Logitech
|
||||
0x056e, // Elecom
|
||||
0x06a3, // Saitek
|
||||
0x0738, // Mad Catz
|
||||
0x07ff, // Mad Catz
|
||||
0x0e6f, // PDP
|
||||
0x0f0d, // Hori
|
||||
0x1038, // SteelSeries
|
||||
0x11c9, // Nacon
|
||||
0x12ab, // Unknown
|
||||
0x1430, // RedOctane
|
||||
0x146b, // BigBen
|
||||
0x1532, // Razer Sabertooth
|
||||
0x15e4, // Numark
|
||||
0x162e, // Joytech
|
||||
0x1689, // Razer Onza
|
||||
0x1949, // Lab126, Inc.
|
||||
0x1bad, // Harmonix
|
||||
0x20d6, // PowerA
|
||||
0x24c6, // PowerA
|
||||
0x2c22, // Qanba
|
||||
0x2dc8, // 8BitDo
|
||||
0x9886, // ASTRO Gaming
|
||||
};
|
||||
|
||||
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||
usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
|
||||
(usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
|
||||
usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
|
||||
int vendor_id = usbDevice.getVendorId();
|
||||
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||
if (vendor_id == supportedVid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||
final int XB1_IFACE_SUBCLASS = 71;
|
||||
final int XB1_IFACE_PROTOCOL = 208;
|
||||
final int[] SUPPORTED_VENDORS = {
|
||||
0x03f0, // HP
|
||||
0x044f, // Thrustmaster
|
||||
0x045e, // Microsoft
|
||||
0x0738, // Mad Catz
|
||||
0x0b05, // ASUS
|
||||
0x0e6f, // PDP
|
||||
0x0f0d, // Hori
|
||||
0x10f5, // Turtle Beach
|
||||
0x1532, // Razer Wildcat
|
||||
0x20d6, // PowerA
|
||||
0x24c6, // PowerA
|
||||
0x294b, // Snakebyte
|
||||
0x2dc8, // 8BitDo
|
||||
0x2e24, // Hyperkin
|
||||
0x2e95, // SCUF
|
||||
0x3285, // Nacon
|
||||
0x3537, // GameSir
|
||||
0x366c, // ByoWave
|
||||
};
|
||||
|
||||
if (usbInterface.getId() == 0 &&
|
||||
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||
usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
|
||||
usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
|
||||
int vendor_id = usbDevice.getVendorId();
|
||||
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||
if (vendor_id == supportedVid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleUsbDeviceAttached(UsbDevice usbDevice) {
|
||||
connectHIDDeviceUSB(usbDevice);
|
||||
}
|
||||
|
||||
private void handleUsbDeviceDetached(UsbDevice usbDevice) {
|
||||
List<Integer> devices = new ArrayList<Integer>();
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
if (usbDevice.equals(device.getDevice())) {
|
||||
devices.add(device.getId());
|
||||
}
|
||||
}
|
||||
for (int id : devices) {
|
||||
HIDDevice device = mDevicesById.get(id);
|
||||
mDevicesById.remove(id);
|
||||
device.shutdown();
|
||||
HIDDeviceDisconnected(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
if (usbDevice.equals(device.getDevice())) {
|
||||
boolean opened = false;
|
||||
if (permission_granted) {
|
||||
opened = device.open();
|
||||
}
|
||||
HIDDeviceOpenResult(device.getId(), opened);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void connectHIDDeviceUSB(UsbDevice usbDevice) {
|
||||
synchronized (this) {
|
||||
int interface_mask = 0;
|
||||
for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
|
||||
UsbInterface usbInterface = usbDevice.getInterface(interface_index);
|
||||
if (isHIDDeviceInterface(usbDevice, usbInterface)) {
|
||||
// Check to see if we've already added this interface
|
||||
// This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
|
||||
int interface_id = usbInterface.getId();
|
||||
if ((interface_mask & (1 << interface_id)) != 0) {
|
||||
continue;
|
||||
}
|
||||
interface_mask |= (1 << interface_id);
|
||||
|
||||
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
|
||||
int id = device.getId();
|
||||
mDevicesById.put(id, device);
|
||||
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeBluetooth() {
|
||||
Log.d(TAG, "Initializing Bluetooth");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 31 /* Android 12 */ &&
|
||||
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH_CONNECT, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH_CONNECT");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ &&
|
||||
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
|
||||
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find bonded bluetooth controllers and create SteamControllers for them
|
||||
mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
if (mBluetoothManager == null) {
|
||||
// This device doesn't support Bluetooth.
|
||||
return;
|
||||
}
|
||||
|
||||
BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
|
||||
if (btAdapter == null) {
|
||||
// This device has Bluetooth support in the codebase, but has no available adapters.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get our bonded devices.
|
||||
for (BluetoothDevice device : btAdapter.getBondedDevices()) {
|
||||
|
||||
Log.d(TAG, "Bluetooth device available: " + device);
|
||||
if (isSteamController(device)) {
|
||||
connectBluetoothDevice(device);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// NOTE: These don't work on Chromebooks, to my undying dismay.
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||
if (Build.VERSION.SDK_INT >= 33) { /* Android 13.0 (TIRAMISU) */
|
||||
mContext.registerReceiver(mBluetoothBroadcast, filter, Context.RECEIVER_EXPORTED);
|
||||
} else {
|
||||
mContext.registerReceiver(mBluetoothBroadcast, filter);
|
||||
}
|
||||
|
||||
if (mIsChromebook) {
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
|
||||
|
||||
// final HIDDeviceManager finalThis = this;
|
||||
// mHandler.postDelayed(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// finalThis.chromebookConnectionHandler();
|
||||
// }
|
||||
// }, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownBluetooth() {
|
||||
try {
|
||||
mContext.unregisterReceiver(mBluetoothBroadcast);
|
||||
} catch (Exception e) {
|
||||
// We may not have registered, that's okay
|
||||
}
|
||||
}
|
||||
|
||||
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
|
||||
// This function provides a sort of dummy version of that, watching for changes in the
|
||||
// connected devices and attempting to add controllers as things change.
|
||||
void chromebookConnectionHandler() {
|
||||
if (!mIsChromebook) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
|
||||
ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
|
||||
|
||||
List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
|
||||
|
||||
for (BluetoothDevice bluetoothDevice : currentConnected) {
|
||||
if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
|
||||
connected.add(bluetoothDevice);
|
||||
}
|
||||
}
|
||||
for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
|
||||
if (!currentConnected.contains(bluetoothDevice)) {
|
||||
disconnected.add(bluetoothDevice);
|
||||
}
|
||||
}
|
||||
|
||||
mLastBluetoothDevices = currentConnected;
|
||||
|
||||
for (BluetoothDevice bluetoothDevice : disconnected) {
|
||||
disconnectBluetoothDevice(bluetoothDevice);
|
||||
}
|
||||
for (BluetoothDevice bluetoothDevice : connected) {
|
||||
connectBluetoothDevice(bluetoothDevice);
|
||||
}
|
||||
|
||||
final HIDDeviceManager finalThis = this;
|
||||
mHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finalThis.chromebookConnectionHandler();
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
|
||||
synchronized (this) {
|
||||
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
|
||||
Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
|
||||
|
||||
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||
device.reconnect();
|
||||
|
||||
return false;
|
||||
}
|
||||
HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
|
||||
int id = device.getId();
|
||||
mBluetoothDevices.put(bluetoothDevice, device);
|
||||
mDevicesById.put(id, device);
|
||||
|
||||
// The Steam Controller will mark itself connected once initialization is complete
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||
synchronized (this) {
|
||||
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||
if (device == null)
|
||||
return;
|
||||
|
||||
int id = device.getId();
|
||||
mBluetoothDevices.remove(bluetoothDevice);
|
||||
mDevicesById.remove(id);
|
||||
device.shutdown();
|
||||
HIDDeviceDisconnected(id);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSteamController(BluetoothDevice bluetoothDevice) {
|
||||
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
|
||||
if (bluetoothDevice == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the device has no local name, we really don't want to try an equality check against it.
|
||||
if (bluetoothDevice.getName() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
|
||||
}
|
||||
|
||||
private void close() {
|
||||
shutdownUSB();
|
||||
shutdownBluetooth();
|
||||
synchronized (this) {
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
device.shutdown();
|
||||
}
|
||||
mDevicesById.clear();
|
||||
mBluetoothDevices.clear();
|
||||
HIDDeviceReleaseCallback();
|
||||
}
|
||||
}
|
||||
|
||||
public void setFrozen(boolean frozen) {
|
||||
synchronized (this) {
|
||||
for (HIDDevice device : mDevicesById.values()) {
|
||||
device.setFrozen(frozen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private HIDDevice getDevice(int id) {
|
||||
synchronized (this) {
|
||||
HIDDevice result = mDevicesById.get(id);
|
||||
if (result == null) {
|
||||
Log.v(TAG, "No device for id: " + id);
|
||||
Log.v(TAG, "Available devices: " + mDevicesById.keySet());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////// JNI interface functions
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
boolean initialize(boolean usb, boolean bluetooth) {
|
||||
Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
|
||||
|
||||
if (usb) {
|
||||
initializeUSB();
|
||||
}
|
||||
if (bluetooth) {
|
||||
initializeBluetooth();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean openDevice(int deviceID) {
|
||||
Log.v(TAG, "openDevice deviceID=" + deviceID);
|
||||
HIDDevice device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Look to see if this is a USB device and we have permission to access it
|
||||
UsbDevice usbDevice = device.getDevice();
|
||||
if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
|
||||
HIDDeviceOpenPending(deviceID);
|
||||
try {
|
||||
final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
|
||||
int flags;
|
||||
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
|
||||
flags = FLAG_MUTABLE;
|
||||
} else {
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||
intent.setPackage(mContext.getPackageName());
|
||||
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, intent, flags));
|
||||
} catch (Exception e) {
|
||||
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
|
||||
HIDDeviceOpenResult(deviceID, false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return device.open();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int writeReport(int deviceID, byte[] report, boolean feature) {
|
||||
try {
|
||||
//Log.v(TAG, "writeReport deviceID=" + deviceID + " length=" + report.length);
|
||||
HIDDevice device;
|
||||
device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return device.writeReport(report, feature);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
boolean readReport(int deviceID, byte[] report, boolean feature) {
|
||||
try {
|
||||
//Log.v(TAG, "readReport deviceID=" + deviceID);
|
||||
HIDDevice device;
|
||||
device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return false;
|
||||
}
|
||||
|
||||
return device.readReport(report, feature);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void closeDevice(int deviceID) {
|
||||
try {
|
||||
Log.v(TAG, "closeDevice deviceID=" + deviceID);
|
||||
HIDDevice device;
|
||||
device = getDevice(deviceID);
|
||||
if (device == null) {
|
||||
HIDDeviceDisconnected(deviceID);
|
||||
return;
|
||||
}
|
||||
|
||||
device.close();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////// Native methods
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private native void HIDDeviceRegisterCallback();
|
||||
private native void HIDDeviceReleaseCallback();
|
||||
|
||||
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol, boolean bBluetooth);
|
||||
native void HIDDeviceOpenPending(int deviceID);
|
||||
native void HIDDeviceOpenResult(int deviceID, boolean opened);
|
||||
native void HIDDeviceDisconnected(int deviceID);
|
||||
|
||||
native void HIDDeviceInputReport(int deviceID, byte[] report);
|
||||
native void HIDDeviceReportResponse(int deviceID, byte[] report);
|
||||
}
|
||||
@ -0,0 +1,313 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.hardware.usb.*;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
class HIDDeviceUSB implements HIDDevice {
|
||||
|
||||
private static final String TAG = "hidapi";
|
||||
|
||||
protected HIDDeviceManager mManager;
|
||||
protected UsbDevice mDevice;
|
||||
protected int mInterfaceIndex;
|
||||
protected int mInterface;
|
||||
protected int mDeviceId;
|
||||
protected UsbDeviceConnection mConnection;
|
||||
protected UsbEndpoint mInputEndpoint;
|
||||
protected UsbEndpoint mOutputEndpoint;
|
||||
protected InputThread mInputThread;
|
||||
protected boolean mRunning;
|
||||
protected boolean mFrozen;
|
||||
|
||||
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
|
||||
mManager = manager;
|
||||
mDevice = usbDevice;
|
||||
mInterfaceIndex = interface_index;
|
||||
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
|
||||
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
|
||||
mRunning = false;
|
||||
}
|
||||
|
||||
String getIdentifier() {
|
||||
return String.format(Locale.ENGLISH, "%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return mDeviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVendorId() {
|
||||
return mDevice.getVendorId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProductId() {
|
||||
return mDevice.getProductId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerialNumber() {
|
||||
String result = null;
|
||||
try {
|
||||
result = mDevice.getSerialNumber();
|
||||
}
|
||||
catch (SecurityException exception) {
|
||||
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
|
||||
}
|
||||
if (result == null) {
|
||||
result = "";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturerName() {
|
||||
String result;
|
||||
result = mDevice.getManufacturerName();
|
||||
if (result == null) {
|
||||
result = String.format("%x", getVendorId());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProductName() {
|
||||
String result;
|
||||
result = mDevice.getProductName();
|
||||
if (result == null) {
|
||||
result = String.format("%x", getProductId());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbDevice getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
String getDeviceName() {
|
||||
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean open() {
|
||||
mConnection = mManager.getUSBManager().openDevice(mDevice);
|
||||
if (mConnection == null) {
|
||||
Log.w(TAG, "Unable to open USB device " + getDeviceName());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Force claim our interface
|
||||
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||
if (!mConnection.claimInterface(iface, true)) {
|
||||
Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the endpoints
|
||||
for (int j = 0; j < iface.getEndpointCount(); j++) {
|
||||
UsbEndpoint endpt = iface.getEndpoint(j);
|
||||
switch (endpt.getDirection()) {
|
||||
case UsbConstants.USB_DIR_IN:
|
||||
if (mInputEndpoint == null) {
|
||||
mInputEndpoint = endpt;
|
||||
}
|
||||
break;
|
||||
case UsbConstants.USB_DIR_OUT:
|
||||
if (mOutputEndpoint == null) {
|
||||
mOutputEndpoint = endpt;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the required endpoints were present
|
||||
if (mInputEndpoint == null || mOutputEndpoint == null) {
|
||||
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start listening for input
|
||||
mRunning = true;
|
||||
mInputThread = new InputThread();
|
||||
mInputThread.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int writeReport(byte[] report, boolean feature) {
|
||||
if (mConnection == null) {
|
||||
Log.w(TAG, "writeReport() called with no device connection");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (feature) {
|
||||
int res = -1;
|
||||
int offset = 0;
|
||||
int length = report.length;
|
||||
boolean skipped_report_id = false;
|
||||
byte report_number = report[0];
|
||||
|
||||
if (report_number == 0x0) {
|
||||
++offset;
|
||||
--length;
|
||||
skipped_report_id = true;
|
||||
}
|
||||
|
||||
res = mConnection.controlTransfer(
|
||||
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
|
||||
0x09/*HID set_report*/,
|
||||
(3/*HID feature*/ << 8) | report_number,
|
||||
mInterface,
|
||||
report, offset, length,
|
||||
1000/*timeout millis*/);
|
||||
|
||||
if (res < 0) {
|
||||
Log.w(TAG, "writeFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (skipped_report_id) {
|
||||
++length;
|
||||
}
|
||||
return length;
|
||||
} else {
|
||||
int res = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
|
||||
if (res != report.length) {
|
||||
Log.w(TAG, "writeOutputReport() returned " + res + " on device " + getDeviceName());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readReport(byte[] report, boolean feature) {
|
||||
int res = -1;
|
||||
int offset = 0;
|
||||
int length = report.length;
|
||||
boolean skipped_report_id = false;
|
||||
byte report_number = report[0];
|
||||
|
||||
if (mConnection == null) {
|
||||
Log.w(TAG, "readReport() called with no device connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (report_number == 0x0) {
|
||||
/* Offset the return buffer by 1, so that the report ID
|
||||
will remain in byte 0. */
|
||||
++offset;
|
||||
--length;
|
||||
skipped_report_id = true;
|
||||
}
|
||||
|
||||
res = mConnection.controlTransfer(
|
||||
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
|
||||
0x01/*HID get_report*/,
|
||||
((feature ? 3/*HID feature*/ : 1/*HID Input*/) << 8) | report_number,
|
||||
mInterface,
|
||||
report, offset, length,
|
||||
1000/*timeout millis*/);
|
||||
|
||||
if (res < 0) {
|
||||
Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (skipped_report_id) {
|
||||
++res;
|
||||
++length;
|
||||
}
|
||||
|
||||
byte[] data;
|
||||
if (res == length) {
|
||||
data = report;
|
||||
} else {
|
||||
data = Arrays.copyOfRange(report, 0, res);
|
||||
}
|
||||
mManager.HIDDeviceReportResponse(mDeviceId, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mRunning = false;
|
||||
if (mInputThread != null) {
|
||||
while (mInputThread.isAlive()) {
|
||||
mInputThread.interrupt();
|
||||
try {
|
||||
mInputThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
// Keep trying until we're done
|
||||
}
|
||||
}
|
||||
mInputThread = null;
|
||||
}
|
||||
if (mConnection != null) {
|
||||
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||
mConnection.releaseInterface(iface);
|
||||
mConnection.close();
|
||||
mConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
close();
|
||||
mManager = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrozen(boolean frozen) {
|
||||
mFrozen = frozen;
|
||||
}
|
||||
|
||||
protected class InputThread extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
int packetSize = mInputEndpoint.getMaxPacketSize();
|
||||
byte[] packet = new byte[packetSize];
|
||||
while (mRunning) {
|
||||
int r;
|
||||
try
|
||||
{
|
||||
r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
|
||||
break;
|
||||
}
|
||||
if (r < 0) {
|
||||
// Could be a timeout or an I/O error
|
||||
}
|
||||
if (r > 0) {
|
||||
byte[] data;
|
||||
if (r == packetSize) {
|
||||
data = packet;
|
||||
} else {
|
||||
data = Arrays.copyOfRange(packet, 0, r);
|
||||
}
|
||||
|
||||
if (!mFrozen) {
|
||||
mManager.HIDDeviceInputReport(mDeviceId, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
SDL library initialization
|
||||
*/
|
||||
public class SDL {
|
||||
|
||||
// This function should be called first and sets up the native code
|
||||
// so it can call into the Java classes
|
||||
static public void setupJNI() {
|
||||
SDLActivity.nativeSetupJNI();
|
||||
SDLAudioManager.nativeSetupJNI();
|
||||
SDLControllerManager.nativeSetupJNI();
|
||||
}
|
||||
|
||||
// This function should be called each time the activity is started
|
||||
static public void initialize() {
|
||||
setContext(null);
|
||||
|
||||
SDLActivity.initialize();
|
||||
SDLAudioManager.initialize();
|
||||
SDLControllerManager.initialize();
|
||||
}
|
||||
|
||||
// This function stores the current activity (SDL or not)
|
||||
static public void setContext(Activity context) {
|
||||
SDLAudioManager.setContext(context);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
static public Activity getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||
loadLibrary(libraryName, mContext);
|
||||
}
|
||||
|
||||
static void loadLibrary(String libraryName, Context context) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||
|
||||
if (libraryName == null) {
|
||||
throw new NullPointerException("No library name provided.");
|
||||
}
|
||||
|
||||
try {
|
||||
// Let's see if we have ReLinker available in the project. This is necessary for
|
||||
// some projects that have huge numbers of local libraries bundled, and thus may
|
||||
// trip a bug in Android's native library loader which ReLinker works around. (If
|
||||
// loadLibrary works properly, ReLinker will simply use the normal Android method
|
||||
// internally.)
|
||||
//
|
||||
// To use ReLinker, just add it as a dependency. For more information, see
|
||||
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
|
||||
//
|
||||
Class<?> relinkClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
|
||||
Class<?> relinkListenerClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
|
||||
Class<?> contextClass = context.getClassLoader().loadClass("android.content.Context");
|
||||
Class<?> stringClass = context.getClassLoader().loadClass("java.lang.String");
|
||||
|
||||
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
|
||||
// they've changed during updates.
|
||||
Method forceMethod = relinkClass.getDeclaredMethod("force");
|
||||
Object relinkInstance = forceMethod.invoke(null);
|
||||
Class<?> relinkInstanceClass = relinkInstance.getClass();
|
||||
|
||||
// Actually load the library!
|
||||
Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
|
||||
loadMethod.invoke(relinkInstance, context, libraryName, null, null);
|
||||
}
|
||||
catch (final Throwable e) {
|
||||
// Fall back
|
||||
try {
|
||||
System.loadLibrary(libraryName);
|
||||
}
|
||||
catch (final UnsatisfiedLinkError ule) {
|
||||
throw ule;
|
||||
}
|
||||
catch (final SecurityException se) {
|
||||
throw se;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static Activity mContext;
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioDeviceCallback;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
|
||||
class SDLAudioManager {
|
||||
protected static final String TAG = "SDLAudio";
|
||||
|
||||
protected static Context mContext;
|
||||
|
||||
private static AudioDeviceCallback mAudioDeviceCallback;
|
||||
|
||||
static void initialize() {
|
||||
mAudioDeviceCallback = null;
|
||||
|
||||
if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */)
|
||||
{
|
||||
mAudioDeviceCallback = new AudioDeviceCallback() {
|
||||
@Override
|
||||
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
|
||||
for (AudioDeviceInfo deviceInfo : addedDevices) {
|
||||
nativeAddAudioDevice(deviceInfo.isSink(), deviceInfo.getProductName().toString(), deviceInfo.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
|
||||
for (AudioDeviceInfo deviceInfo : removedDevices) {
|
||||
nativeRemoveAudioDevice(deviceInfo.isSink(), deviceInfo.getId());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static void setContext(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
static void release(Context context) {
|
||||
// no-op atm
|
||||
}
|
||||
|
||||
// Audio
|
||||
|
||||
private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
for (AudioDeviceInfo deviceInfo : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
|
||||
if (deviceInfo.getId() == deviceId) {
|
||||
return deviceInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static AudioDeviceInfo getPlaybackAudioDeviceInfo(int deviceId) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
for (AudioDeviceInfo deviceInfo : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
|
||||
if (deviceInfo.getId() == deviceId) {
|
||||
return deviceInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static void registerAudioDeviceCallback() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
// get an initial list now, before hotplug callbacks fire.
|
||||
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) {
|
||||
if (dev.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
|
||||
continue; // Device cannot be opened
|
||||
}
|
||||
nativeAddAudioDevice(dev.isSink(), dev.getProductName().toString(), dev.getId());
|
||||
}
|
||||
for (AudioDeviceInfo dev : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) {
|
||||
nativeAddAudioDevice(dev.isSink(), dev.getProductName().toString(), dev.getId());
|
||||
}
|
||||
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
|
||||
}
|
||||
}
|
||||
|
||||
static void unregisterAudioDeviceCallback() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called by SDL using JNI. */
|
||||
static void audioSetThreadPriority(boolean recording, int device_id) {
|
||||
try {
|
||||
|
||||
/* Set thread name */
|
||||
if (recording) {
|
||||
Thread.currentThread().setName("SDLAudioC" + device_id);
|
||||
} else {
|
||||
Thread.currentThread().setName("SDLAudioP" + device_id);
|
||||
}
|
||||
|
||||
/* Set thread priority */
|
||||
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.v(TAG, "modify thread properties failed " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
static native void nativeSetupJNI();
|
||||
|
||||
static native void nativeRemoveAudioDevice(boolean recording, int deviceId);
|
||||
|
||||
static native void nativeAddAudioDevice(boolean recording, String name, int deviceId);
|
||||
|
||||
}
|
||||
@ -0,0 +1,877 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.os.VibratorManager;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
|
||||
public class SDLControllerManager
|
||||
{
|
||||
|
||||
static native void nativeSetupJNI();
|
||||
|
||||
static native void nativeAddJoystick(int device_id, String name, String desc,
|
||||
int vendor_id, int product_id,
|
||||
int button_mask,
|
||||
int naxes, int axis_mask, int nhats, boolean can_rumble);
|
||||
static native void nativeRemoveJoystick(int device_id);
|
||||
static native void nativeAddHaptic(int device_id, String name);
|
||||
static native void nativeRemoveHaptic(int device_id);
|
||||
static public native boolean onNativePadDown(int device_id, int keycode);
|
||||
static public native boolean onNativePadUp(int device_id, int keycode);
|
||||
static native void onNativeJoy(int device_id, int axis,
|
||||
float value);
|
||||
static native void onNativeHat(int device_id, int hat_id,
|
||||
int x, int y);
|
||||
|
||||
protected static SDLJoystickHandler mJoystickHandler;
|
||||
protected static SDLHapticHandler mHapticHandler;
|
||||
|
||||
private static final String TAG = "SDLControllerManager";
|
||||
|
||||
static void initialize() {
|
||||
if (mJoystickHandler == null) {
|
||||
mJoystickHandler = new SDLJoystickHandler();
|
||||
}
|
||||
|
||||
if (mHapticHandler == null) {
|
||||
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
|
||||
mHapticHandler = new SDLHapticHandler_API31();
|
||||
} else if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
|
||||
mHapticHandler = new SDLHapticHandler_API26();
|
||||
} else {
|
||||
mHapticHandler = new SDLHapticHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
|
||||
static public boolean handleJoystickMotionEvent(MotionEvent event) {
|
||||
return mJoystickHandler.handleMotionEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
static void pollInputDevices() {
|
||||
mJoystickHandler.pollInputDevices();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
static void pollHapticDevices() {
|
||||
mHapticHandler.pollHapticDevices();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
static void hapticRun(int device_id, float intensity, int length) {
|
||||
mHapticHandler.run(device_id, intensity, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
static void hapticRumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length) {
|
||||
mHapticHandler.rumble(device_id, low_frequency_intensity, high_frequency_intensity, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
static void hapticStop(int device_id)
|
||||
{
|
||||
mHapticHandler.stop(device_id);
|
||||
}
|
||||
|
||||
// Check if a given device is considered a possible SDL joystick
|
||||
static public boolean isDeviceSDLJoystick(int deviceId) {
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
// We cannot use InputDevice.isVirtual before API 16, so let's accept
|
||||
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
|
||||
if ((device == null) || (deviceId < 0)) {
|
||||
return false;
|
||||
}
|
||||
int sources = device.getSources();
|
||||
|
||||
/* This is called for every button press, so let's not spam the logs */
|
||||
/*
|
||||
if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||
Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
|
||||
}
|
||||
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
|
||||
Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
|
||||
}
|
||||
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
|
||||
Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
|
||||
}
|
||||
*/
|
||||
|
||||
return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
|
||||
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
|
||||
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Actual joystick functionality available for API >= 19 devices */
|
||||
class SDLJoystickHandler {
|
||||
|
||||
static class SDLJoystick {
|
||||
int device_id;
|
||||
String name;
|
||||
String desc;
|
||||
ArrayList<InputDevice.MotionRange> axes;
|
||||
ArrayList<InputDevice.MotionRange> hats;
|
||||
}
|
||||
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
|
||||
@Override
|
||||
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
|
||||
// Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
|
||||
int arg0Axis = arg0.getAxis();
|
||||
int arg1Axis = arg1.getAxis();
|
||||
if (arg0Axis == MotionEvent.AXIS_GAS) {
|
||||
arg0Axis = MotionEvent.AXIS_BRAKE;
|
||||
} else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
|
||||
arg0Axis = MotionEvent.AXIS_GAS;
|
||||
}
|
||||
if (arg1Axis == MotionEvent.AXIS_GAS) {
|
||||
arg1Axis = MotionEvent.AXIS_BRAKE;
|
||||
} else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
|
||||
arg1Axis = MotionEvent.AXIS_GAS;
|
||||
}
|
||||
|
||||
// Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
|
||||
// This is because the usual pairing are:
|
||||
// - AXIS_X + AXIS_Y (left stick).
|
||||
// - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
|
||||
// - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
|
||||
// This sorts the axes in the above order, which tends to be correct
|
||||
// for Xbox-ish game pads that have the right stick on RX/RY and the
|
||||
// triggers on Z/RZ.
|
||||
//
|
||||
// Gamepads that don't have AXIS_Z/AXIS_RZ but use
|
||||
// AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
|
||||
//
|
||||
// References:
|
||||
// - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
|
||||
// - https://www.kernel.org/doc/html/latest/input/gamepad.html
|
||||
if (arg0Axis == MotionEvent.AXIS_Z) {
|
||||
arg0Axis = MotionEvent.AXIS_RZ - 1;
|
||||
} else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
|
||||
--arg0Axis;
|
||||
}
|
||||
if (arg1Axis == MotionEvent.AXIS_Z) {
|
||||
arg1Axis = MotionEvent.AXIS_RZ - 1;
|
||||
} else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
|
||||
--arg1Axis;
|
||||
}
|
||||
|
||||
return arg0Axis - arg1Axis;
|
||||
}
|
||||
}
|
||||
|
||||
private final ArrayList<SDLJoystick> mJoysticks;
|
||||
|
||||
SDLJoystickHandler() {
|
||||
|
||||
mJoysticks = new ArrayList<SDLJoystick>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles adding and removing of input devices.
|
||||
*/
|
||||
void pollInputDevices() {
|
||||
int[] deviceIds = InputDevice.getDeviceIds();
|
||||
|
||||
for (int device_id : deviceIds) {
|
||||
if (SDLControllerManager.isDeviceSDLJoystick(device_id)) {
|
||||
SDLJoystick joystick = getJoystick(device_id);
|
||||
if (joystick == null) {
|
||||
InputDevice joystickDevice = InputDevice.getDevice(device_id);
|
||||
joystick = new SDLJoystick();
|
||||
joystick.device_id = device_id;
|
||||
joystick.name = joystickDevice.getName();
|
||||
joystick.desc = getJoystickDescriptor(joystickDevice);
|
||||
joystick.axes = new ArrayList<InputDevice.MotionRange>();
|
||||
joystick.hats = new ArrayList<InputDevice.MotionRange>();
|
||||
|
||||
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
|
||||
Collections.sort(ranges, new RangeComparator());
|
||||
for (InputDevice.MotionRange range : ranges) {
|
||||
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
|
||||
joystick.hats.add(range);
|
||||
} else {
|
||||
joystick.axes.add(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean can_rumble = false;
|
||||
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
|
||||
VibratorManager manager = joystickDevice.getVibratorManager();
|
||||
int[] vibrators = manager.getVibratorIds();
|
||||
if (vibrators.length > 0) {
|
||||
can_rumble = true;
|
||||
}
|
||||
}
|
||||
|
||||
mJoysticks.add(joystick);
|
||||
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
|
||||
getVendorId(joystickDevice), getProductId(joystickDevice),
|
||||
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check removed devices */
|
||||
ArrayList<Integer> removedDevices = null;
|
||||
for (SDLJoystick joystick : mJoysticks) {
|
||||
int device_id = joystick.device_id;
|
||||
int i;
|
||||
for (i = 0; i < deviceIds.length; i++) {
|
||||
if (device_id == deviceIds[i]) break;
|
||||
}
|
||||
if (i == deviceIds.length) {
|
||||
if (removedDevices == null) {
|
||||
removedDevices = new ArrayList<Integer>();
|
||||
}
|
||||
removedDevices.add(device_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (removedDevices != null) {
|
||||
for (int device_id : removedDevices) {
|
||||
SDLControllerManager.nativeRemoveJoystick(device_id);
|
||||
for (int i = 0; i < mJoysticks.size(); i++) {
|
||||
if (mJoysticks.get(i).device_id == device_id) {
|
||||
mJoysticks.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected SDLJoystick getJoystick(int device_id) {
|
||||
for (SDLJoystick joystick : mJoysticks) {
|
||||
if (joystick.device_id == device_id) {
|
||||
return joystick;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles given MotionEvent.
|
||||
* @param event the event to be handled.
|
||||
* @return if given event was processed.
|
||||
*/
|
||||
boolean handleMotionEvent(MotionEvent event) {
|
||||
int actionPointerIndex = event.getActionIndex();
|
||||
int action = event.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_MOVE) {
|
||||
SDLJoystick joystick = getJoystick(event.getDeviceId());
|
||||
if (joystick != null) {
|
||||
for (int i = 0; i < joystick.axes.size(); i++) {
|
||||
InputDevice.MotionRange range = joystick.axes.get(i);
|
||||
/* Normalize the value to -1...1 */
|
||||
float value = (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
|
||||
SDLControllerManager.onNativeJoy(joystick.device_id, i, value);
|
||||
}
|
||||
for (int i = 0; i < joystick.hats.size() / 2; i++) {
|
||||
int hatX = Math.round(event.getAxisValue(joystick.hats.get(2 * i).getAxis(), actionPointerIndex));
|
||||
int hatY = Math.round(event.getAxisValue(joystick.hats.get(2 * i + 1).getAxis(), actionPointerIndex));
|
||||
SDLControllerManager.onNativeHat(joystick.device_id, i, hatX, hatY);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
String getJoystickDescriptor(InputDevice joystickDevice) {
|
||||
String desc = joystickDevice.getDescriptor();
|
||||
|
||||
if (desc != null && !desc.isEmpty()) {
|
||||
return desc;
|
||||
}
|
||||
|
||||
return joystickDevice.getName();
|
||||
}
|
||||
|
||||
int getProductId(InputDevice joystickDevice) {
|
||||
return joystickDevice.getProductId();
|
||||
}
|
||||
|
||||
int getVendorId(InputDevice joystickDevice) {
|
||||
return joystickDevice.getVendorId();
|
||||
}
|
||||
|
||||
int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||
// For compatibility, keep computing the axis mask like before,
|
||||
// only really distinguishing 2, 4 and 6 axes.
|
||||
int axis_mask = 0;
|
||||
if (ranges.size() >= 2) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY))
|
||||
axis_mask |= 0x0003;
|
||||
}
|
||||
if (ranges.size() >= 4) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY))
|
||||
axis_mask |= 0x000c;
|
||||
}
|
||||
if (ranges.size() >= 6) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))
|
||||
axis_mask |= 0x0030;
|
||||
}
|
||||
// Also add an indicator bit for whether the sorting order has changed.
|
||||
// This serves to disable outdated gamecontrollerdb.txt mappings.
|
||||
boolean have_z = false;
|
||||
boolean have_past_z_before_rz = false;
|
||||
for (InputDevice.MotionRange range : ranges) {
|
||||
int axis = range.getAxis();
|
||||
if (axis == MotionEvent.AXIS_Z) {
|
||||
have_z = true;
|
||||
} else if (axis > MotionEvent.AXIS_Z && axis < MotionEvent.AXIS_RZ) {
|
||||
have_past_z_before_rz = true;
|
||||
}
|
||||
}
|
||||
if (have_z && have_past_z_before_rz) {
|
||||
// If both these exist, the compare() function changed sorting order.
|
||||
// Set a bit to indicate this fact.
|
||||
axis_mask |= 0x8000;
|
||||
}
|
||||
return axis_mask;
|
||||
}
|
||||
|
||||
int getButtonMask(InputDevice joystickDevice) {
|
||||
int button_mask = 0;
|
||||
int[] keys = new int[] {
|
||||
KeyEvent.KEYCODE_BUTTON_A,
|
||||
KeyEvent.KEYCODE_BUTTON_B,
|
||||
KeyEvent.KEYCODE_BUTTON_X,
|
||||
KeyEvent.KEYCODE_BUTTON_Y,
|
||||
KeyEvent.KEYCODE_BACK,
|
||||
KeyEvent.KEYCODE_MENU,
|
||||
KeyEvent.KEYCODE_BUTTON_MODE,
|
||||
KeyEvent.KEYCODE_BUTTON_START,
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBL,
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBR,
|
||||
KeyEvent.KEYCODE_BUTTON_L1,
|
||||
KeyEvent.KEYCODE_BUTTON_R1,
|
||||
KeyEvent.KEYCODE_DPAD_UP,
|
||||
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||
KeyEvent.KEYCODE_BUTTON_SELECT,
|
||||
KeyEvent.KEYCODE_DPAD_CENTER,
|
||||
|
||||
// These don't map into any SDL controller buttons directly
|
||||
KeyEvent.KEYCODE_BUTTON_L2,
|
||||
KeyEvent.KEYCODE_BUTTON_R2,
|
||||
KeyEvent.KEYCODE_BUTTON_C,
|
||||
KeyEvent.KEYCODE_BUTTON_Z,
|
||||
KeyEvent.KEYCODE_BUTTON_1,
|
||||
KeyEvent.KEYCODE_BUTTON_2,
|
||||
KeyEvent.KEYCODE_BUTTON_3,
|
||||
KeyEvent.KEYCODE_BUTTON_4,
|
||||
KeyEvent.KEYCODE_BUTTON_5,
|
||||
KeyEvent.KEYCODE_BUTTON_6,
|
||||
KeyEvent.KEYCODE_BUTTON_7,
|
||||
KeyEvent.KEYCODE_BUTTON_8,
|
||||
KeyEvent.KEYCODE_BUTTON_9,
|
||||
KeyEvent.KEYCODE_BUTTON_10,
|
||||
KeyEvent.KEYCODE_BUTTON_11,
|
||||
KeyEvent.KEYCODE_BUTTON_12,
|
||||
KeyEvent.KEYCODE_BUTTON_13,
|
||||
KeyEvent.KEYCODE_BUTTON_14,
|
||||
KeyEvent.KEYCODE_BUTTON_15,
|
||||
KeyEvent.KEYCODE_BUTTON_16,
|
||||
};
|
||||
int[] masks = new int[] {
|
||||
(1 << 0), // A -> A
|
||||
(1 << 1), // B -> B
|
||||
(1 << 2), // X -> X
|
||||
(1 << 3), // Y -> Y
|
||||
(1 << 4), // BACK -> BACK
|
||||
(1 << 6), // MENU -> START
|
||||
(1 << 5), // MODE -> GUIDE
|
||||
(1 << 6), // START -> START
|
||||
(1 << 7), // THUMBL -> LEFTSTICK
|
||||
(1 << 8), // THUMBR -> RIGHTSTICK
|
||||
(1 << 9), // L1 -> LEFTSHOULDER
|
||||
(1 << 10), // R1 -> RIGHTSHOULDER
|
||||
(1 << 11), // DPAD_UP -> DPAD_UP
|
||||
(1 << 12), // DPAD_DOWN -> DPAD_DOWN
|
||||
(1 << 13), // DPAD_LEFT -> DPAD_LEFT
|
||||
(1 << 14), // DPAD_RIGHT -> DPAD_RIGHT
|
||||
(1 << 4), // SELECT -> BACK
|
||||
(1 << 0), // DPAD_CENTER -> A
|
||||
(1 << 15), // L2 -> ??
|
||||
(1 << 16), // R2 -> ??
|
||||
(1 << 17), // C -> ??
|
||||
(1 << 18), // Z -> ??
|
||||
(1 << 20), // 1 -> ??
|
||||
(1 << 21), // 2 -> ??
|
||||
(1 << 22), // 3 -> ??
|
||||
(1 << 23), // 4 -> ??
|
||||
(1 << 24), // 5 -> ??
|
||||
(1 << 25), // 6 -> ??
|
||||
(1 << 26), // 7 -> ??
|
||||
(1 << 27), // 8 -> ??
|
||||
(1 << 28), // 9 -> ??
|
||||
(1 << 29), // 10 -> ??
|
||||
(1 << 30), // 11 -> ??
|
||||
(1 << 31), // 12 -> ??
|
||||
// We're out of room...
|
||||
0xFFFFFFFF, // 13 -> ??
|
||||
0xFFFFFFFF, // 14 -> ??
|
||||
0xFFFFFFFF, // 15 -> ??
|
||||
0xFFFFFFFF, // 16 -> ??
|
||||
};
|
||||
boolean[] has_keys = joystickDevice.hasKeys(keys);
|
||||
for (int i = 0; i < keys.length; ++i) {
|
||||
if (has_keys[i]) {
|
||||
button_mask |= masks[i];
|
||||
}
|
||||
}
|
||||
return button_mask;
|
||||
}
|
||||
}
|
||||
|
||||
class SDLHapticHandler_API31 extends SDLHapticHandler {
|
||||
@Override
|
||||
void run(int device_id, float intensity, int length) {
|
||||
SDLHaptic haptic = getHaptic(device_id);
|
||||
if (haptic != null) {
|
||||
vibrate(haptic.vib, intensity, length);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void rumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length) {
|
||||
InputDevice device = InputDevice.getDevice(device_id);
|
||||
if (device == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return;
|
||||
}
|
||||
|
||||
VibratorManager manager = device.getVibratorManager();
|
||||
int[] vibrators = manager.getVibratorIds();
|
||||
if (vibrators.length >= 2) {
|
||||
vibrate(manager.getVibrator(vibrators[0]), low_frequency_intensity, length);
|
||||
vibrate(manager.getVibrator(vibrators[1]), high_frequency_intensity, length);
|
||||
} else if (vibrators.length == 1) {
|
||||
float intensity = (low_frequency_intensity * 0.6f) + (high_frequency_intensity * 0.4f);
|
||||
vibrate(manager.getVibrator(vibrators[0]), intensity, length);
|
||||
}
|
||||
}
|
||||
|
||||
private void vibrate(Vibrator vibrator, float intensity, int length) {
|
||||
|
||||
if (Build.VERSION.SDK_INT < 31 /* Android 12.0 (S) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return;
|
||||
}
|
||||
|
||||
if (intensity == 0.0f) {
|
||||
vibrator.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
int value = Math.round(intensity * 255);
|
||||
if (value > 255) {
|
||||
value = 255;
|
||||
}
|
||||
if (value < 1) {
|
||||
vibrator.cancel();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
vibrator.vibrate(VibrationEffect.createOneShot(length, value));
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
|
||||
// something went horribly wrong with the Android 8.0 APIs.
|
||||
vibrator.vibrate(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SDLHapticHandler_API26 extends SDLHapticHandler {
|
||||
@Override
|
||||
void run(int device_id, float intensity, int length) {
|
||||
|
||||
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return;
|
||||
}
|
||||
|
||||
SDLHaptic haptic = getHaptic(device_id);
|
||||
if (haptic != null) {
|
||||
if (intensity == 0.0f) {
|
||||
stop(device_id);
|
||||
return;
|
||||
}
|
||||
|
||||
int vibeValue = Math.round(intensity * 255);
|
||||
|
||||
if (vibeValue > 255) {
|
||||
vibeValue = 255;
|
||||
}
|
||||
if (vibeValue < 1) {
|
||||
stop(device_id);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
|
||||
// something went horribly wrong with the Android 8.0 APIs.
|
||||
haptic.vib.vibrate(length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SDLHapticHandler {
|
||||
|
||||
static class SDLHaptic {
|
||||
int device_id;
|
||||
String name;
|
||||
Vibrator vib;
|
||||
}
|
||||
|
||||
private final ArrayList<SDLHaptic> mHaptics;
|
||||
|
||||
SDLHapticHandler() {
|
||||
mHaptics = new ArrayList<SDLHaptic>();
|
||||
}
|
||||
|
||||
void run(int device_id, float intensity, int length) {
|
||||
SDLHaptic haptic = getHaptic(device_id);
|
||||
if (haptic != null) {
|
||||
haptic.vib.vibrate(length);
|
||||
}
|
||||
}
|
||||
|
||||
void rumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length) {
|
||||
// Not supported in older APIs
|
||||
}
|
||||
|
||||
void stop(int device_id) {
|
||||
SDLHaptic haptic = getHaptic(device_id);
|
||||
if (haptic != null) {
|
||||
haptic.vib.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void pollHapticDevices() {
|
||||
|
||||
final int deviceId_VIBRATOR_SERVICE = 999999;
|
||||
boolean hasVibratorService = false;
|
||||
|
||||
/* Check VIBRATOR_SERVICE */
|
||||
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
if (vib != null) {
|
||||
hasVibratorService = vib.hasVibrator();
|
||||
|
||||
if (hasVibratorService) {
|
||||
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
|
||||
if (haptic == null) {
|
||||
haptic = new SDLHaptic();
|
||||
haptic.device_id = deviceId_VIBRATOR_SERVICE;
|
||||
haptic.name = "VIBRATOR_SERVICE";
|
||||
haptic.vib = vib;
|
||||
mHaptics.add(haptic);
|
||||
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check removed devices */
|
||||
ArrayList<Integer> removedDevices = null;
|
||||
for (SDLHaptic haptic : mHaptics) {
|
||||
int device_id = haptic.device_id;
|
||||
if (device_id != deviceId_VIBRATOR_SERVICE || !hasVibratorService) {
|
||||
if (removedDevices == null) {
|
||||
removedDevices = new ArrayList<Integer>();
|
||||
}
|
||||
removedDevices.add(device_id);
|
||||
} // else: don't remove the vibrator if it is still present
|
||||
}
|
||||
|
||||
if (removedDevices != null) {
|
||||
for (int device_id : removedDevices) {
|
||||
SDLControllerManager.nativeRemoveHaptic(device_id);
|
||||
for (int i = 0; i < mHaptics.size(); i++) {
|
||||
if (mHaptics.get(i).device_id == device_id) {
|
||||
mHaptics.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected SDLHaptic getHaptic(int device_id) {
|
||||
for (SDLHaptic haptic : mHaptics) {
|
||||
if (haptic.device_id == device_id) {
|
||||
return haptic;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class SDLGenericMotionListener_API14 implements View.OnGenericMotionListener {
|
||||
protected static final int SDL_PEN_DEVICE_TYPE_UNKNOWN = 0;
|
||||
protected static final int SDL_PEN_DEVICE_TYPE_DIRECT = 1;
|
||||
protected static final int SDL_PEN_DEVICE_TYPE_INDIRECT = 2;
|
||||
|
||||
// Generic Motion (mouse hover, joystick...) events go here
|
||||
@Override
|
||||
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||
if (event.getSource() == InputDevice.SOURCE_JOYSTICK)
|
||||
return SDLControllerManager.handleJoystickMotionEvent(event);
|
||||
|
||||
float x, y;
|
||||
int action = event.getActionMasked();
|
||||
int pointerCount = event.getPointerCount();
|
||||
boolean consumed = false;
|
||||
|
||||
for (int i = 0; i < pointerCount; i++) {
|
||||
int toolType = event.getToolType(i);
|
||||
|
||||
if (toolType == MotionEvent.TOOL_TYPE_MOUSE) {
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, i);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, i);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
x = getEventX(event, i);
|
||||
y = getEventY(event, i);
|
||||
|
||||
SDLActivity.onNativeMouse(0, action, x, y, checkRelativeEvent(event));
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (toolType == MotionEvent.TOOL_TYPE_STYLUS || toolType == MotionEvent.TOOL_TYPE_ERASER) {
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_HOVER_ENTER:
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_HOVER_EXIT:
|
||||
x = event.getX(i);
|
||||
y = event.getY(i);
|
||||
float p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
|
||||
// BUTTON_STYLUS_PRIMARY is 2^5, so shift by 4, and apply SDL_PEN_INPUT_DOWN/SDL_PEN_INPUT_ERASER_TIP
|
||||
int buttons = (event.getButtonState() >> 4) | (1 << (toolType == MotionEvent.TOOL_TYPE_STYLUS ? 0 : 30));
|
||||
|
||||
SDLActivity.onNativePen(event.getPointerId(i), getPenDeviceType(event.getDevice()), buttons, action, x, y, p);
|
||||
consumed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
boolean supportsRelativeMouse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean inRelativeMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean setRelativeMouseEnabled(boolean enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void reclaimRelativeMouseModeIfNeeded() {
|
||||
|
||||
}
|
||||
|
||||
boolean checkRelativeEvent(MotionEvent event) {
|
||||
return inRelativeMode();
|
||||
}
|
||||
|
||||
float getEventX(MotionEvent event, int pointerIndex) {
|
||||
return event.getX(pointerIndex);
|
||||
}
|
||||
|
||||
float getEventY(MotionEvent event, int pointerIndex) {
|
||||
return event.getY(pointerIndex);
|
||||
}
|
||||
|
||||
int getPenDeviceType(InputDevice penDevice) {
|
||||
return SDL_PEN_DEVICE_TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API14 {
|
||||
// Generic Motion (mouse hover, joystick...) events go here
|
||||
|
||||
private boolean mRelativeModeEnabled;
|
||||
|
||||
@Override
|
||||
boolean supportsRelativeMouse() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean inRelativeMode() {
|
||||
return mRelativeModeEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean setRelativeMouseEnabled(boolean enabled) {
|
||||
mRelativeModeEnabled = enabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
float getEventX(MotionEvent event, int pointerIndex) {
|
||||
if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mRelativeModeEnabled && event.getToolType(pointerIndex) == MotionEvent.TOOL_TYPE_MOUSE) {
|
||||
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X, pointerIndex);
|
||||
} else {
|
||||
return event.getX(pointerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
float getEventY(MotionEvent event, int pointerIndex) {
|
||||
if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mRelativeModeEnabled && event.getToolType(pointerIndex) == MotionEvent.TOOL_TYPE_MOUSE) {
|
||||
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y, pointerIndex);
|
||||
} else {
|
||||
return event.getY(pointerIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
|
||||
// Generic Motion (mouse hover, joystick...) events go here
|
||||
private boolean mRelativeModeEnabled;
|
||||
|
||||
@Override
|
||||
boolean supportsRelativeMouse() {
|
||||
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean inRelativeMode() {
|
||||
return mRelativeModeEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean setRelativeMouseEnabled(boolean enabled) {
|
||||
|
||||
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
|
||||
if (enabled) {
|
||||
SDLActivity.getContentView().requestPointerCapture();
|
||||
} else {
|
||||
SDLActivity.getContentView().releasePointerCapture();
|
||||
}
|
||||
mRelativeModeEnabled = enabled;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void reclaimRelativeMouseModeIfNeeded() {
|
||||
|
||||
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
|
||||
SDLActivity.getContentView().requestPointerCapture();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean checkRelativeEvent(MotionEvent event) {
|
||||
if (Build.VERSION.SDK_INT < 26 /* Android 8.0 (O) */) {
|
||||
/* Silence 'lint' warning */
|
||||
return false;
|
||||
}
|
||||
return event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
float getEventX(MotionEvent event, int pointerIndex) {
|
||||
// Relative mouse in capture mode will only have relative for X/Y
|
||||
return event.getX(pointerIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
float getEventY(MotionEvent event, int pointerIndex) {
|
||||
// Relative mouse in capture mode will only have relative for X/Y
|
||||
return event.getY(pointerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
class SDLGenericMotionListener_API29 extends SDLGenericMotionListener_API26 {
|
||||
@Override
|
||||
int getPenDeviceType(InputDevice penDevice)
|
||||
{
|
||||
if (penDevice == null) {
|
||||
return SDL_PEN_DEVICE_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
return penDevice.isExternal() ? SDL_PEN_DEVICE_TYPE_INDIRECT : SDL_PEN_DEVICE_TYPE_DIRECT;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.content.*;
|
||||
import android.text.InputType;
|
||||
import android.view.*;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
/* This is a fake invisible editor view that receives the input and defines the
|
||||
* pan&scan region
|
||||
*/
|
||||
public class SDLDummyEdit extends View implements View.OnKeyListener
|
||||
{
|
||||
InputConnection ic;
|
||||
int input_type;
|
||||
|
||||
SDLDummyEdit(Context context) {
|
||||
super(context);
|
||||
setFocusableInTouchMode(true);
|
||||
setFocusable(true);
|
||||
setOnKeyListener(this);
|
||||
}
|
||||
|
||||
void setInputType(int input_type) {
|
||||
this.input_type = input_type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCheckIsTextEditor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
return SDLActivity.handleKeyEvent(v, keyCode, event, ic);
|
||||
}
|
||||
|
||||
//
|
||||
@Override
|
||||
public boolean onKeyPreIme (int keyCode, KeyEvent event) {
|
||||
// As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
|
||||
// FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
|
||||
// FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
|
||||
// FIXME: A more effective solution would be to assume our Layout to be RelativeLayout or LinearLayout
|
||||
// FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
|
||||
// FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
|
||||
if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (SDLActivity.mTextEdit != null && SDLActivity.mTextEdit.getVisibility() == View.VISIBLE) {
|
||||
SDLActivity.onNativeKeyboardFocusLost();
|
||||
}
|
||||
}
|
||||
return super.onKeyPreIme(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
ic = new SDLInputConnection(this, true);
|
||||
|
||||
outAttrs.inputType = input_type;
|
||||
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI |
|
||||
EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
|
||||
|
||||
return ic;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,138 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
import android.content.*;
|
||||
import android.os.Build;
|
||||
import android.text.Editable;
|
||||
import android.view.*;
|
||||
import android.view.inputmethod.BaseInputConnection;
|
||||
import android.widget.EditText;
|
||||
|
||||
class SDLInputConnection extends BaseInputConnection
|
||||
{
|
||||
protected EditText mEditText;
|
||||
protected String mCommittedText = "";
|
||||
|
||||
SDLInputConnection(View targetView, boolean fullEditor) {
|
||||
super(targetView, fullEditor);
|
||||
mEditText = new EditText(SDL.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable getEditable() {
|
||||
return mEditText.getEditableText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendKeyEvent(KeyEvent event) {
|
||||
/*
|
||||
* This used to handle the keycodes from soft keyboard (and IME-translated input from hardkeyboard)
|
||||
* However, as of Ice Cream Sandwich and later, almost all soft keyboard doesn't generate key presses
|
||||
* and so we need to generate them ourselves in commitText. To avoid duplicates on the handful of keys
|
||||
* that still do, we empty this out.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Return DOES still generate a key event, however. So rather than using it as the 'click a button' key
|
||||
* as we do with physical keyboards, let's just use it to hide the keyboard.
|
||||
*/
|
||||
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
|
||||
if (SDLActivity.onNativeSoftReturnKey()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return super.sendKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||||
if (!super.commitText(text, newCursorPosition)) {
|
||||
return false;
|
||||
}
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
||||
if (!super.setComposingText(text, newCursorPosition)) {
|
||||
return false;
|
||||
}
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
||||
if (Build.VERSION.SDK_INT <= 29 /* Android 10.0 (Q) */) {
|
||||
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions>/14560344/android-backspace-in-webview-baseinputconnection
|
||||
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
|
||||
if (beforeLength > 0 && afterLength == 0) {
|
||||
// backspace(s)
|
||||
while (beforeLength-- > 0) {
|
||||
nativeGenerateScancodeForUnichar('\b');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!super.deleteSurroundingText(beforeLength, afterLength)) {
|
||||
return false;
|
||||
}
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void updateText() {
|
||||
final Editable content = getEditable();
|
||||
if (content == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String text = content.toString();
|
||||
int compareLength = Math.min(text.length(), mCommittedText.length());
|
||||
int matchLength, offset;
|
||||
|
||||
/* Backspace over characters that are no longer in the string */
|
||||
for (matchLength = 0; matchLength < compareLength; ) {
|
||||
int codePoint = mCommittedText.codePointAt(matchLength);
|
||||
if (codePoint != text.codePointAt(matchLength)) {
|
||||
break;
|
||||
}
|
||||
matchLength += Character.charCount(codePoint);
|
||||
}
|
||||
/* FIXME: This doesn't handle graphemes, like '🌬️' */
|
||||
for (offset = matchLength; offset < mCommittedText.length(); ) {
|
||||
int codePoint = mCommittedText.codePointAt(offset);
|
||||
nativeGenerateScancodeForUnichar('\b');
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
|
||||
if (matchLength < text.length()) {
|
||||
String pendingText = text.subSequence(matchLength, text.length()).toString();
|
||||
if (!SDLActivity.dispatchingKeyEvent()) {
|
||||
for (offset = 0; offset < pendingText.length(); ) {
|
||||
int codePoint = pendingText.codePointAt(offset);
|
||||
if (codePoint == '\n') {
|
||||
if (SDLActivity.onNativeSoftReturnKey()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Higher code points don't generate simulated scancodes */
|
||||
if (codePoint > 0 && codePoint < 128) {
|
||||
nativeGenerateScancodeForUnichar((char)codePoint);
|
||||
}
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
}
|
||||
SDLInputConnection.nativeCommitText(pendingText, 0);
|
||||
}
|
||||
mCommittedText = text;
|
||||
}
|
||||
|
||||
public static native void nativeCommitText(String text, int newCursorPosition);
|
||||
|
||||
public static native void nativeGenerateScancodeForUnichar(char c);
|
||||
}
|
||||
|
||||
@ -0,0 +1,446 @@
|
||||
package org.libsdl.app;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Insets;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.PointerIcon;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import android.view.ScaleGestureDetector;
|
||||
|
||||
/**
|
||||
SDLSurface. This is what we draw on, so we need to know when it's created
|
||||
in order to do anything useful.
|
||||
|
||||
Because of this, that's where we set up the SDL thread
|
||||
*/
|
||||
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
View.OnApplyWindowInsetsListener, View.OnKeyListener, View.OnTouchListener,
|
||||
SensorEventListener, ScaleGestureDetector.OnScaleGestureListener {
|
||||
|
||||
// Sensors
|
||||
protected SensorManager mSensorManager;
|
||||
protected Display mDisplay;
|
||||
|
||||
// Keep track of the surface size to normalize touch events
|
||||
protected float mWidth, mHeight;
|
||||
|
||||
// Is SurfaceView ready for rendering
|
||||
protected boolean mIsSurfaceReady;
|
||||
|
||||
// Pinch events
|
||||
private final ScaleGestureDetector scaleGestureDetector;
|
||||
|
||||
// Startup
|
||||
protected SDLSurface(Context context) {
|
||||
super(context);
|
||||
getHolder().addCallback(this);
|
||||
|
||||
scaleGestureDetector = new ScaleGestureDetector(context, this);
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnApplyWindowInsetsListener(this);
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
|
||||
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
setOnGenericMotionListener(SDLActivity.getMotionListener());
|
||||
|
||||
// Some arbitrary defaults to avoid a potential division by zero
|
||||
mWidth = 1.0f;
|
||||
mHeight = 1.0f;
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
}
|
||||
|
||||
protected void handlePause() {
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
||||
}
|
||||
|
||||
protected void handleResume() {
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnApplyWindowInsetsListener(this);
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||
}
|
||||
|
||||
protected Surface getNativeSurface() {
|
||||
return getHolder().getSurface();
|
||||
}
|
||||
|
||||
// Called when we have a valid drawing surface
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceCreated()");
|
||||
SDLActivity.onNativeSurfaceCreated();
|
||||
}
|
||||
|
||||
// Called when we lose the surface
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceDestroyed()");
|
||||
|
||||
// Transition to pause, if needed
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||
SDLActivity.handleNativeState();
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
SDLActivity.onNativeSurfaceDestroyed();
|
||||
}
|
||||
|
||||
// Called when the surface is resized
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder,
|
||||
int format, int width, int height) {
|
||||
Log.v("SDL", "surfaceChanged()");
|
||||
|
||||
if (SDLActivity.mSingleton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
int nDeviceWidth = width;
|
||||
int nDeviceHeight = height;
|
||||
float density = 1.0f;
|
||||
try
|
||||
{
|
||||
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||
mDisplay.getRealMetrics( realMetrics );
|
||||
nDeviceWidth = realMetrics.widthPixels;
|
||||
nDeviceHeight = realMetrics.heightPixels;
|
||||
// Use densityDpi instead of density to more closely match what the UI scale is
|
||||
density = (float)realMetrics.densityDpi / 160.0f;
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
synchronized(SDLActivity.getContext()) {
|
||||
// In case we're waiting on a size change after going fullscreen, send a notification.
|
||||
SDLActivity.getContext().notifyAll();
|
||||
}
|
||||
|
||||
Log.v("SDL", "Window size: " + width + "x" + height);
|
||||
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
|
||||
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, density, mDisplay.getRefreshRate());
|
||||
SDLActivity.onNativeResize();
|
||||
|
||||
// Prevent a screen distortion glitch,
|
||||
// for instance when the device is in Landscape and a Portrait App is resumed.
|
||||
boolean skip = false;
|
||||
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
|
||||
|
||||
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
|
||||
if (mWidth > mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
|
||||
if (mWidth < mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Special Patch for Square Resolution: Black Berry Passport
|
||||
if (skip) {
|
||||
double min = Math.min(mWidth, mHeight);
|
||||
double max = Math.max(mWidth, mHeight);
|
||||
|
||||
if (max / min < 1.20) {
|
||||
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't skip if we might be multi-window or have popup dialogs
|
||||
if (skip) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
Log.v("SDL", "Skip .. Surface is not ready.");
|
||||
mIsSurfaceReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
|
||||
SDLActivity.onNativeSurfaceChanged();
|
||||
|
||||
/* Surface is ready */
|
||||
mIsSurfaceReady = true;
|
||||
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||
SDLActivity.handleNativeState();
|
||||
}
|
||||
|
||||
// Window inset
|
||||
@Override
|
||||
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
|
||||
if (Build.VERSION.SDK_INT >= 30 /* Android 11 (R) */) {
|
||||
Insets combined = insets.getInsets(WindowInsets.Type.systemBars() |
|
||||
WindowInsets.Type.systemGestures() |
|
||||
WindowInsets.Type.mandatorySystemGestures() |
|
||||
WindowInsets.Type.tappableElement() |
|
||||
WindowInsets.Type.displayCutout());
|
||||
|
||||
SDLActivity.onNativeInsetsChanged(combined.left, combined.right, combined.top, combined.bottom);
|
||||
}
|
||||
|
||||
// Pass these to any child views in case they need them
|
||||
return insets;
|
||||
}
|
||||
|
||||
// Key events
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
|
||||
}
|
||||
|
||||
private float getNormalizedX(float x)
|
||||
{
|
||||
if (mWidth <= 1) {
|
||||
return 0.5f;
|
||||
} else {
|
||||
return (x / (mWidth - 1));
|
||||
}
|
||||
}
|
||||
|
||||
private float getNormalizedY(float y)
|
||||
{
|
||||
if (mHeight <= 1) {
|
||||
return 0.5f;
|
||||
} else {
|
||||
return (y / (mHeight - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Touch events
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
/* Ref: http://developer.android.com/training/gestures/multi.html */
|
||||
int touchDevId = event.getDeviceId();
|
||||
final int pointerCount = event.getPointerCount();
|
||||
int action = event.getActionMasked();
|
||||
int pointerId;
|
||||
int i = 0;
|
||||
float x,y,p;
|
||||
|
||||
if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN)
|
||||
i = event.getActionIndex();
|
||||
|
||||
do {
|
||||
int toolType = event.getToolType(i);
|
||||
|
||||
if (toolType == MotionEvent.TOOL_TYPE_MOUSE) {
|
||||
int buttonState = event.getButtonState();
|
||||
boolean relative = false;
|
||||
|
||||
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
|
||||
// if we are. We'll leverage our existing mouse motion listener
|
||||
SDLGenericMotionListener_API14 motionListener = SDLActivity.getMotionListener();
|
||||
x = motionListener.getEventX(event, i);
|
||||
y = motionListener.getEventY(event, i);
|
||||
relative = motionListener.inRelativeMode();
|
||||
|
||||
SDLActivity.onNativeMouse(buttonState, action, x, y, relative);
|
||||
} else if (toolType == MotionEvent.TOOL_TYPE_STYLUS || toolType == MotionEvent.TOOL_TYPE_ERASER) {
|
||||
pointerId = event.getPointerId(i);
|
||||
x = event.getX(i);
|
||||
y = event.getY(i);
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
|
||||
// BUTTON_STYLUS_PRIMARY is 2^5, so shift by 4, and apply SDL_PEN_INPUT_DOWN/SDL_PEN_INPUT_ERASER_TIP
|
||||
int buttonState = (event.getButtonState() >> 4) | (1 << (toolType == MotionEvent.TOOL_TYPE_STYLUS ? 0 : 30));
|
||||
|
||||
SDLActivity.onNativePen(pointerId, SDLActivity.getMotionListener().getPenDeviceType(event.getDevice()), buttonState, action, x, y, p);
|
||||
} else { // MotionEvent.TOOL_TYPE_FINGER or MotionEvent.TOOL_TYPE_UNKNOWN
|
||||
pointerId = event.getPointerId(i);
|
||||
x = getNormalizedX(event.getX(i));
|
||||
y = getNormalizedY(event.getY(i));
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerId, action, x, y, p);
|
||||
}
|
||||
|
||||
// Non-primary up/down
|
||||
if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_POINTER_DOWN)
|
||||
break;
|
||||
} while (++i < pointerCount);
|
||||
|
||||
scaleGestureDetector.onTouchEvent(event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sensor events
|
||||
protected void enableSensor(int sensortype, boolean enabled) {
|
||||
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
||||
if (enabled) {
|
||||
mSensorManager.registerListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype),
|
||||
SensorManager.SENSOR_DELAY_GAME, null);
|
||||
} else {
|
||||
mSensorManager.unregisterListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||
|
||||
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
|
||||
// We thus should check here.
|
||||
int newRotation;
|
||||
|
||||
float x, y;
|
||||
switch (mDisplay.getRotation()) {
|
||||
case Surface.ROTATION_0:
|
||||
default:
|
||||
x = event.values[0];
|
||||
y = event.values[1];
|
||||
newRotation = 0;
|
||||
break;
|
||||
case Surface.ROTATION_90:
|
||||
x = -event.values[1];
|
||||
y = event.values[0];
|
||||
newRotation = 90;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
x = -event.values[0];
|
||||
y = -event.values[1];
|
||||
newRotation = 180;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
x = event.values[1];
|
||||
y = -event.values[0];
|
||||
newRotation = 270;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newRotation != SDLActivity.mCurrentRotation) {
|
||||
SDLActivity.mCurrentRotation = newRotation;
|
||||
SDLActivity.onNativeRotationChanged(newRotation);
|
||||
}
|
||||
|
||||
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
|
||||
y / SensorManager.GRAVITY_EARTH,
|
||||
event.values[2] / SensorManager.GRAVITY_EARTH);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent android internal NullPointerException (https://github.com/libsdl-org/SDL/issues/13306)
|
||||
@Override
|
||||
public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
|
||||
try {
|
||||
return super.onResolvePointerIcon(event, pointerIndex);
|
||||
} catch (NullPointerException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Captured pointer events for API 26.
|
||||
@Override
|
||||
public boolean onCapturedPointerEvent(MotionEvent event)
|
||||
{
|
||||
int action = event.getActionMasked();
|
||||
int pointerCount = event.getPointerCount();
|
||||
|
||||
for (int i = 0; i < pointerCount; i++) {
|
||||
float x, y;
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, i);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, i);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
x = event.getX(i);
|
||||
y = event.getY(i);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||
|
||||
// Change our action value to what SDL's code expects.
|
||||
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
|
||||
action = MotionEvent.ACTION_DOWN;
|
||||
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
|
||||
action = MotionEvent.ACTION_UP;
|
||||
}
|
||||
|
||||
x = event.getX(i);
|
||||
y = event.getY(i);
|
||||
int button = event.getButtonState();
|
||||
|
||||
SDLActivity.onNativeMouse(button, action, x, y, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScale(ScaleGestureDetector detector) {
|
||||
float scale = detector.getScaleFactor();
|
||||
SDLActivity.onNativePinchUpdate(scale);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector detector) {
|
||||
SDLActivity.onNativePinchStart();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScaleEnd(ScaleGestureDetector detector) {
|
||||
SDLActivity.onNativePinchEnd();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 916 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
||||
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#000000</color>
|
||||
</resources>
|
||||
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">IAEGame</string>
|
||||
</resources>
|
||||
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.IAEGame" parent="android:Theme.Material.Light.NoActionBar" />
|
||||
</resources>
|
||||
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older than API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
||||
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
||||
6
Tools/Platform/Android/AndroidProject/build.gradle.kts
Normal file
@ -0,0 +1,6 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
alias(libs.plugins.android.application) apply false
|
||||
alias(libs.plugins.kotlin.android) apply false
|
||||
alias(libs.plugins.kotlin.compose) apply false
|
||||
}
|
||||
23
Tools/Platform/Android/AndroidProject/gradle.properties
Normal file
@ -0,0 +1,23 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. For more details, visit
|
||||
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
@ -0,0 +1,32 @@
|
||||
[versions]
|
||||
agp = "8.13.0"
|
||||
kotlin = "2.0.21"
|
||||
coreKtx = "1.17.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.3.0"
|
||||
espressoCore = "3.7.0"
|
||||
lifecycleRuntimeKtx = "2.9.4"
|
||||
activityCompose = "1.11.0"
|
||||
composeBom = "2024.09.00"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
|
||||
BIN
Tools/Platform/Android/AndroidProject/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
Tools/Platform/Android/AndroidProject/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#Fri Oct 17 23:05:59 IST 2025
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
185
Tools/Platform/Android/AndroidProject/gradlew
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
89
Tools/Platform/Android/AndroidProject/gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
23
Tools/Platform/Android/AndroidProject/settings.gradle.kts
Normal file
@ -0,0 +1,23 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google {
|
||||
content {
|
||||
includeGroupByRegex("com\\.android.*")
|
||||
includeGroupByRegex("com\\.google.*")
|
||||
includeGroupByRegex("androidx.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "IAEGame"
|
||||
include(":app")
|
||||
150
Tools/Platform/Android/android-ndk-r27d/CHANGELOG.md
Normal file
@ -0,0 +1,150 @@
|
||||
# Changelog
|
||||
|
||||
Report issues to [GitHub].
|
||||
|
||||
For Android Studio issues, go to https://b.android.com and file a bug using the
|
||||
Android Studio component, not the NDK component.
|
||||
|
||||
If you're a build system maintainer that needs to use the tools in the NDK
|
||||
directly, see the [build system maintainers guide].
|
||||
|
||||
[GitHub]: https://github.com/android/ndk/issues
|
||||
[build system maintainers guide]: https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md
|
||||
|
||||
## Announcements
|
||||
|
||||
* Android V will allow OEMs to ship arm64-v8a devices with 16KiB page sizes.
|
||||
Devices that use this configuration will not be able to run existing apps that
|
||||
use native code. To be compatible with these devices, applications will need
|
||||
to rebuild all their native code to be 16KiB aligned, and rewrite any code
|
||||
which assumes a specific page size. ndk-build and CMake have options to enable
|
||||
this mode (see note about `APP_SUPPORT_FLEXIBLE_PAGE_SIZES` and
|
||||
`ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES` below). A future version of the NDK will
|
||||
enable this mode by default. If you're using or maintaining a third-party
|
||||
build system, consult the [build system maintainers guide] for instructions.
|
||||
|
||||
See [Support 16 KB page sizes] for more information.
|
||||
|
||||
[Support 16 KB page sizes]: https://developer.android.com/guide/practices/page-sizes
|
||||
|
||||
## r27d
|
||||
|
||||
* Updated LLVM to clang-r522817d. See `clang_source_info.md` in the toolchain
|
||||
directory for version information.
|
||||
* [Issue 2144]: Fixed issue where `lldb.sh` did not work when installed to a
|
||||
path which contained spaces.
|
||||
* [Issue 2143]: Fixed lldb.sh not finding libpython on macOS.
|
||||
|
||||
[Issue 2143]: https://github.com/android/ndk/issues/2143
|
||||
[Issue 2144]: https://github.com/android/ndk/issues/2144
|
||||
|
||||
## r27c
|
||||
|
||||
* Updated LLVM to clang-r522817c. See `clang_source_info.md` in the toolchain
|
||||
directory for version information.
|
||||
* [Issue 2040]: Further fixes for miscompiles of indirect gotos.
|
||||
* [Issue 2064]: Fix HWAsan miscompilation.
|
||||
* [Issue 2070]: Fix crash in instantiation of function definitions.
|
||||
* [Issue 2084]: Fix for incorrect compiler error on overloaded member
|
||||
functions with ref-qualifiers.
|
||||
|
||||
[Issue 2040]: https://github.com/android/ndk/issues/2040
|
||||
[Issue 2064]: https://github.com/android/ndk/issues/2064
|
||||
[Issue 2070]: https://github.com/android/ndk/issues/2070
|
||||
[Issue 2084]: https://github.com/android/ndk/issues/2084
|
||||
|
||||
## r27b
|
||||
|
||||
* Updated LLVM to clang-r522817b. See `clang_source_info.md` in the toolchain
|
||||
directory for version information.
|
||||
* [Issue 2040]: Fixed miscompile with indirect gotos.
|
||||
* [Issue 2054]: Fixed crash in x86 fp16 instruction selection.
|
||||
* [Issue 2059]: Fixed infinite loop in aarch64 vector instruction selection.
|
||||
* [Issue 2032]: Fixed compatibility issues with projects that used a very old
|
||||
`cmake_minimum_required` version ("Policy CMP0057 is not set: Support new
|
||||
IN_LIST if() operator").
|
||||
* [Issue 2039]: Fixed `LOCAL_STRIP_MODE` not being reset by
|
||||
`include $(CLEAR_VARS)`.
|
||||
* [Issue 2049]: Restored metadata used by CMake in non-toolchain-file use cases
|
||||
that used `CMAKE_SYSTEM_PROCESSOR` instead of `CMAKE_ANDROID_ARCH_ABI`. If you
|
||||
ran into this problem, you should switch to using `CMAKE_ANDROID_ARCH_ABI`
|
||||
because that's what [CMake's docs] say to use. Better still, use the NDK's
|
||||
[toolchain file].
|
||||
|
||||
[Issue 2032]: https://github.com/android/ndk/issues/2032
|
||||
[Issue 2039]: https://github.com/android/ndk/issues/2039
|
||||
[Issue 2040]: https://github.com/android/ndk/issues/2040
|
||||
[Issue 2049]: https://github.com/android/ndk/issues/2049
|
||||
[Issue 2054]: https://github.com/android/ndk/issues/2054
|
||||
[Issue 2059]: https://github.com/android/ndk/issues/2059
|
||||
[CMake's docs]: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android-with-the-ndk
|
||||
[toolchain file]: https://developer.android.com/ndk/guides/cmake
|
||||
|
||||
## Changes
|
||||
|
||||
* Updated LLVM to clang-r522817. See `clang_source_info.md` in the toolchain
|
||||
directory for version information.
|
||||
* [Issue 1728]: Clang now emits an error for invalid Android target versions.
|
||||
* [Issue 1853]: `clang-scan-deps` is now included.
|
||||
* [Issue 1911]: Fixed inconsistent `std::regex_replace` results.
|
||||
* [Issue 1947]: Improved support for AArch64 function multi-versioning in clang.
|
||||
* [Issue 1963]: Fixed undefined behavior in `std::unexpected::has_value()`.
|
||||
* [Issue 1988]: Added aarch64 support for `preserve_all` calling convention.
|
||||
* [Issue 2007]: Fixed crash in class template argument deduction caused by
|
||||
self-referential friend declaration.
|
||||
* [Issue 2010]: Removed superfluous libraries to reduce disk use.
|
||||
* [Issue 2012]: Fixed front end crash when using concepts and modules.
|
||||
* [Issue 2013]: Fixed false positive ODR violation in global module fragments.
|
||||
* [Issue 2023]: Fixed Clang crashes related to lambda captures in unevaluated
|
||||
contexts.
|
||||
* [Issue 2024]: Removed invalid `__attribute__((__const__))` from `gettid`.
|
||||
* A RISC-V sysroot (AKA riscv64, or rv64) has been added. It is **not**
|
||||
supported. It is present to aid bringup for OS vendors, but it's not yet a
|
||||
supported Android ABI. It will not be built by default.
|
||||
* [Issue 1856]: Target-prefixed cmd wrappers for clang should now behave
|
||||
appropriately when the first argument includes quotes. **You probably do not
|
||||
need to use those wrappers.** In most cases where you would use
|
||||
`aarch64-linux-android21-clang`, you can instead use `clang -target
|
||||
aarch64-linux-android21`, e.g. `CC="clang -target aarch64-linux-android21"
|
||||
./configure`. The wrappers are only needed when working with systems that do
|
||||
not properly handle a `CC` that includes arguments.
|
||||
* [Issue 1898]: ndk-stack now tolerates 0x prefixed addresses.
|
||||
* [Issue 1921]: `ANDROID_USE_LEGACY_TOOLCHAIN_FILE` value is now preserved
|
||||
during try-compile steps when `ON`.
|
||||
* [Issue 1974]: Unintentionally shipped Vulkan headers have been removed from
|
||||
`sources/third_party/vulkan`. The standard Vulkan headers are included in the
|
||||
Android sysroot, which Clang will find automatically.
|
||||
* [Issue 1993]: ndk-stack now tolerates invalid UTF-8 characters in the trace.
|
||||
* [Issue 1994]: Fixed ndk-gdb/ndk-lldb to use the correct path for
|
||||
make and other tools.
|
||||
* Added `APP_SUPPORT_FLEXIBLE_PAGE_SIZES` for ndk-build and
|
||||
`ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES` for CMake. Set to
|
||||
`APP_SUPPORT_FLEXIBLE_PAGE_SIZES := true` in your `Application.mk` or pass
|
||||
`-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON` to CMake (via
|
||||
`android.defaultConfig.externalNativeBuild.cmake.arguments` if you're using
|
||||
the Android Gradle Plugin) to build your code to be compatible with devices
|
||||
that use a 16KiB page size. Third-party build system users and maintainers
|
||||
should consult the [build system maintainers guide].
|
||||
* Symlinks are now properly preserved in the macOS App Bundle. The NDK installed
|
||||
via that method is now the same size as the one installed via the SDK manager.
|
||||
* The unsupported libclang, libclang-cpp, libLLVM, and libLTO libraries were
|
||||
removed to save space.
|
||||
|
||||
[Issue 1728]: https://github.com/android/ndk/issues/1728
|
||||
[Issue 1853]: https://github.com/android/ndk/issues/1853
|
||||
[Issue 1856]: https://github.com/android/ndk/issues/1856
|
||||
[Issue 1898]: https://github.com/android/ndk/issues/1898
|
||||
[Issue 1911]: https://github.com/android/ndk/issues/1911
|
||||
[Issue 1921]: https://github.com/android/ndk/issues/1921
|
||||
[Issue 1947]: https://github.com/android/ndk/issues/1947
|
||||
[Issue 1963]: https://github.com/android/ndk/issues/1963
|
||||
[Issue 1974]: https://github.com/android/ndk/issues/1974
|
||||
[Issue 1988]: https://github.com/android/ndk/issues/1988
|
||||
[Issue 1993]: https://github.com/android/ndk/issues/1993
|
||||
[Issue 1994]: https://github.com/android/ndk/issues/1994
|
||||
[Issue 2007]: https://github.com/android/ndk/issues/2007
|
||||
[Issue 2010]: https://github.com/android/ndk/issues/2010
|
||||
[Issue 2012]: https://github.com/android/ndk/issues/2012
|
||||
[Issue 2013]: https://github.com/android/ndk/issues/2013
|
||||
[Issue 2023]: https://github.com/android/ndk/issues/2023
|
||||
[Issue 2024]: https://github.com/android/ndk/issues/2024
|
||||
9997
Tools/Platform/Android/android-ndk-r27d/NOTICE
Normal file
15302
Tools/Platform/Android/android-ndk-r27d/NOTICE.toolchain
Normal file
24
Tools/Platform/Android/android-ndk-r27d/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
Android NDK
|
||||
===========
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
NDK documentation, guides, and API reference are available on
|
||||
[our website](https://developer.android.com/ndk/index.html).
|
||||
|
||||
NDK code samples are available on
|
||||
[GitHub](https://github.com/googlesamples/android-ndk).
|
||||
|
||||
Information about Android Studio can be found on [the Android Studio
|
||||
website](https://developer.android.com/studio/index.html).
|
||||
|
||||
Filing Bugs
|
||||
-----------
|
||||
|
||||
NDK bugs should be filed on
|
||||
[GitHub](https://github.com/android-ndk/ndk/issues/new).
|
||||
|
||||
Android Studio and Gradle bugs should be filed in the [Android Studio Bug
|
||||
Tracker](http://b.android.com). For the fastest response, make sure you follow
|
||||
their guide on [Filing Bugs](http://tools.android.com/filing-bugs).
|
||||
52
Tools/Platform/Android/android-ndk-r27d/meta/abis.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"armeabi-v7a": {
|
||||
"bitness": 32,
|
||||
"default": true,
|
||||
"deprecated": false,
|
||||
"proc": "armv7-a",
|
||||
"arch": "arm",
|
||||
"triple": "arm-linux-androideabi",
|
||||
"llvm_triple": "armv7-none-linux-androideabi",
|
||||
"min_os_version": 21
|
||||
},
|
||||
"arm64-v8a": {
|
||||
"bitness": 64,
|
||||
"default": true,
|
||||
"deprecated": false,
|
||||
"proc": "aarch64",
|
||||
"arch": "arm64",
|
||||
"triple": "aarch64-linux-android",
|
||||
"llvm_triple": "aarch64-none-linux-android",
|
||||
"min_os_version": 21
|
||||
},
|
||||
"riscv64": {
|
||||
"bitness": 64,
|
||||
"default": false,
|
||||
"deprecated": false,
|
||||
"proc": "riscv64",
|
||||
"arch": "riscv64",
|
||||
"triple": "riscv64-linux-android",
|
||||
"llvm_triple": "riscv64-none-linux-android",
|
||||
"min_os_version": 35
|
||||
},
|
||||
"x86": {
|
||||
"bitness": 32,
|
||||
"default": true,
|
||||
"deprecated": false,
|
||||
"proc": "i686",
|
||||
"arch": "x86",
|
||||
"triple": "i686-linux-android",
|
||||
"llvm_triple": "i686-none-linux-android",
|
||||
"min_os_version": 21
|
||||
},
|
||||
"x86_64": {
|
||||
"bitness": 64,
|
||||
"default": true,
|
||||
"deprecated": false,
|
||||
"proc": "x86_64",
|
||||
"arch": "x86_64",
|
||||
"triple": "x86_64-linux-android",
|
||||
"llvm_triple": "x86_64-none-linux-android",
|
||||
"min_os_version": 21
|
||||
}
|
||||
}
|
||||
27
Tools/Platform/Android/android-ndk-r27d/meta/platforms.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"min": 21,
|
||||
"max": 35,
|
||||
"aliases": {
|
||||
"20": 19,
|
||||
"25": 24,
|
||||
"J": 16,
|
||||
"J-MR1": 17,
|
||||
"J-MR2": 18,
|
||||
"K": 19,
|
||||
"L": 21,
|
||||
"L-MR1": 22,
|
||||
"M": 23,
|
||||
"N": 24,
|
||||
"N-MR1": 24,
|
||||
"O": 26,
|
||||
"O-MR1": 27,
|
||||
"P": 28,
|
||||
"Q": 29,
|
||||
"R": 30,
|
||||
"S": 31,
|
||||
"Sv2": 32,
|
||||
"Tiramisu": 33,
|
||||
"UpsideDownCake": 34,
|
||||
"VanillaIceCream": 35
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
{
|
||||
"libEGL.so": "21",
|
||||
"libGLESv1_CM.so": "21",
|
||||
"libGLESv2.so": "21",
|
||||
"libGLESv3.so": "21",
|
||||
"libOpenMAXAL.so": "21",
|
||||
"libOpenSLES.so": "21",
|
||||
"libaaudio.so": "26",
|
||||
"libamidi.so": "29",
|
||||
"libandroid.so": "21",
|
||||
"libbinder_ndk.so": "29",
|
||||
"libc.so": "21",
|
||||
"libcamera2ndk.so": "24",
|
||||
"libdl.so": "21",
|
||||
"libicu.so": "31",
|
||||
"libjnigraphics.so": "21",
|
||||
"liblog.so": "21",
|
||||
"libm.so": "21",
|
||||
"libmediandk.so": "21",
|
||||
"libnativehelper.so": "31",
|
||||
"libnativewindow.so": "26",
|
||||
"libneuralnetworks.so": "27",
|
||||
"libstdc++.so": "21",
|
||||
"libsync.so": "26",
|
||||
"libvulkan.so": "24",
|
||||
"libz.so": "21"
|
||||
}
|
||||
7
Tools/Platform/Android/android-ndk-r27d/ndk-build.cmd
Normal file
@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
rem https://stackoverflow.com/a/29057742/632035
|
||||
for /f "tokens=2" %%a in ("%~dp0") do (
|
||||
echo ERROR: NDK path cannot contain spaces
|
||||
exit /b 1
|
||||
)
|
||||
%~dp0build\ndk-build.cmd %*
|
||||
2
Tools/Platform/Android/android-ndk-r27d/ndk-gdb.cmd
Normal file
@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
%~dp0prebuilt\windows-x86_64\bin\ndk-gdb.cmd %*
|
||||
2
Tools/Platform/Android/android-ndk-r27d/ndk-lldb.cmd
Normal file
@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
%~dp0prebuilt\windows-x86_64\bin\ndk-gdb.cmd %*
|
||||
2
Tools/Platform/Android/android-ndk-r27d/ndk-stack.cmd
Normal file
@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
%~dp0prebuilt\windows-x86_64\bin\ndk-stack.cmd %*
|
||||
2
Tools/Platform/Android/android-ndk-r27d/ndk-which.cmd
Normal file
@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
%~dp0prebuilt\windows-x86_64\bin\ndk-which %*
|
||||
@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
setlocal
|
||||
set ANDROID_NDK_PYTHON=%~dp0..\..\..\toolchains\llvm\prebuilt\windows-x86_64\python3\python.exe
|
||||
set SHELL=cmd
|
||||
"%ANDROID_NDK_PYTHON%" -u "%~dp0ndkgdb.pyz" %*
|
||||
@ -0,0 +1,5 @@
|
||||
@echo off
|
||||
setlocal
|
||||
set ANDROID_NDK_PYTHON=%~dp0..\..\..\toolchains\llvm\prebuilt\windows-x86_64\python3\python.exe
|
||||
set SHELL=cmd
|
||||
"%ANDROID_NDK_PYTHON%" -u "%~dp0ndkstack.pyz" %*
|
||||
@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copyright (C) 2012 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
#
|
||||
# DEPRECATED
|
||||
#
|
||||
|
||||
# This script shows the path of the active toolchain components within
|
||||
# the ndk. This was necessary for GCC and binutils, where each ABI had
|
||||
# its own tools, but is not needed for LLVM-based tools which should be
|
||||
# used in preference.
|
||||
|
||||
usage() {
|
||||
echo "USAGE: ndk-which [--abi ABI] TOOL"
|
||||
echo "ABI is 'armeabi-v7a', 'arm64-v8a', 'x86', or 'x86_64'"
|
||||
echo "TOOL is 'gdb', 'objdump', 'readelf', etc."
|
||||
echo
|
||||
echo "Note that LLVM replacements for binutils tools work for all ABIs."
|
||||
exit 1
|
||||
}
|
||||
|
||||
error() {
|
||||
echo "The tool: $1 doesn't exist"
|
||||
echo "Possible choices are: "
|
||||
count=0
|
||||
for file in $2*
|
||||
do
|
||||
if [[ $file == *$1 ]]
|
||||
then
|
||||
echo $file
|
||||
((count = count + 1))
|
||||
fi
|
||||
done
|
||||
if [ $count -eq 0 ]
|
||||
then
|
||||
echo " None "
|
||||
fi
|
||||
exit 1
|
||||
}
|
||||
|
||||
ABI=armeabi-v7a
|
||||
|
||||
while (( "$#" )); do
|
||||
case "$1" in
|
||||
--abi)
|
||||
ABI=$2
|
||||
shift 2
|
||||
abis='^(armeabi-v7a|arm64-v8a|x86|x86_64)$'
|
||||
if [[ ! "$ABI" =~ $abis ]]; then usage; fi
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
TOOL=$1
|
||||
shift
|
||||
|
||||
if [ "$#" != 0 -o "$TOOL" == "" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
# This tool is installed in prebuilt/linux-x86_64/bin/.
|
||||
MYNDKDIR=`dirname $0`/../../..
|
||||
|
||||
# create a temporary skeleton project so that we can leverage build-local.mk
|
||||
TMPDIR=/tmp/ndk-which-$$
|
||||
mkdir -p $TMPDIR/jni
|
||||
cat >$TMPDIR/jni/Android.mk << "END_OF_FILE"
|
||||
include $(CLEAR_VARS)
|
||||
END_OF_FILE
|
||||
|
||||
get_build_var_for_abi() {
|
||||
if [ -z "$GNUMAKE" ] ; then
|
||||
GNUMAKE=make
|
||||
fi
|
||||
NDK_PROJECT_PATH=$TMPDIR $GNUMAKE --no-print-dir -f $MYNDKDIR/build/core/build-local.mk DUMP_$1 APP_ABI=$2
|
||||
}
|
||||
LLVM_TOOLCHAIN_PREFIX=`get_build_var_for_abi LLVM_TOOLCHAIN_PREFIX $ABI`
|
||||
TOOLCHAIN_PREFIX=`get_build_var_for_abi TOOLCHAIN_PREFIX $ABI`
|
||||
rm -Rf $TMPDIR
|
||||
|
||||
# fully qualified file name
|
||||
FQFN=${TOOLCHAIN_PREFIX}$TOOL
|
||||
|
||||
# use the host system's 'which' to decide/report if the file exists or not, and is executable
|
||||
if [ ! -f $FQFN ]
|
||||
then
|
||||
FQFN=${LLVM_TOOLCHAIN_PREFIX}llvm-$TOOL
|
||||
if [ ! -f $FQFN ]
|
||||
then
|
||||
error $TOOL $LLVM_TOOLCHAIN_PREFIX
|
||||
fi
|
||||
fi
|
||||
which "$FQFN"
|
||||
@ -0,0 +1,9 @@
|
||||
#ifndef _YASM_LIBYASM_STDINT_H
|
||||
#define _YASM_LIBYASM_STDINT_H 1
|
||||
#ifndef _GENERATED_STDINT_H
|
||||
#define _GENERATED_STDINT_H "yasm 1.3.0"
|
||||
/* generated using /mnt/disks/build-disk/src/android/ndk-r27-release/prebuilts/clang/host/linux-x86/clang-r522817d/bin/clang --target=x86_64-w64-mingw32 -I/mnt/disks/build-disk/src/android/ndk-r27-release/prebuilts/clang/host/windows-x86/clang-r522817d/include/c++/v1 --sysroot=/mnt/disks/build-disk/src/android/ndk-r27-release/prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32 -L/mnt/disks/build-disk/src/android/ndk-r27-release/prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/lib/gcc/x86_64-w64-mingw32/4.8.3 -B/mnt/disks/build-disk/src/android/ndk-r27-release/prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/lib/gcc/x86_64-w64-mingw32/4.8.3 -L/mnt/disks/build-disk/src/android/ndk-r27-release/prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/lib64 -B/mnt/disks/build-disk/src/android/ndk-r27-release/prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/lib64 -L/mnt/disks/build-disk/src/android/ndk-r27-release/prebuilts/clang/host/windows-x86/clang-r522817d/lib64 -B/mnt/disks/build-disk/src/android/ndk-r27-release/prebuilts/clang/host/windows-x86/clang-r522817d/lib64 -Os -fomit-frame-pointer -w -fuse-ld=lld -s */
|
||||
#define _STDINT_HAVE_STDINT_H 1
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#endif
|
||||
@ -0,0 +1,75 @@
|
||||
/**
|
||||
* \file libyasm.h
|
||||
* \brief YASM library primary header file.
|
||||
*
|
||||
* \license
|
||||
* Copyright (C) 2003-2007 Peter Johnson
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* \endlicense
|
||||
*/
|
||||
#ifndef YASM_LIB_H
|
||||
#define YASM_LIB_H
|
||||
|
||||
#ifdef YASM_PYXELATOR
|
||||
typedef struct __FILE FILE;
|
||||
typedef struct __va_list va_list;
|
||||
typedef unsigned long size_t;
|
||||
typedef unsigned long uintptr_t;
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <libyasm-stdint.h>
|
||||
#endif
|
||||
|
||||
#include <libyasm/compat-queue.h>
|
||||
|
||||
#include <libyasm/coretype.h>
|
||||
#include <libyasm/valparam.h>
|
||||
|
||||
#include <libyasm/linemap.h>
|
||||
|
||||
#include <libyasm/errwarn.h>
|
||||
#include <libyasm/intnum.h>
|
||||
#include <libyasm/floatnum.h>
|
||||
#include <libyasm/expr.h>
|
||||
#include <libyasm/value.h>
|
||||
#include <libyasm/symrec.h>
|
||||
|
||||
#include <libyasm/bytecode.h>
|
||||
#include <libyasm/section.h>
|
||||
#include <libyasm/insn.h>
|
||||
|
||||
#include <libyasm/arch.h>
|
||||
#include <libyasm/dbgfmt.h>
|
||||
#include <libyasm/objfmt.h>
|
||||
#include <libyasm/listfmt.h>
|
||||
#include <libyasm/parser.h>
|
||||
#include <libyasm/preproc.h>
|
||||
|
||||
#include <libyasm/file.h>
|
||||
#include <libyasm/module.h>
|
||||
|
||||
#include <libyasm/hamt.h>
|
||||
#include <libyasm/md5.h>
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,495 @@
|
||||
/**
|
||||
* \file libyasm/arch.h
|
||||
* \brief YASM architecture interface.
|
||||
*
|
||||
* \license
|
||||
* Copyright (C) 2002-2007 Peter Johnson
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* \endlicense
|
||||
*/
|
||||
#ifndef YASM_ARCH_H
|
||||
#define YASM_ARCH_H
|
||||
|
||||
/** Errors that may be returned by yasm_arch_module::create(). */
|
||||
typedef enum yasm_arch_create_error {
|
||||
YASM_ARCH_CREATE_OK = 0, /**< No error. */
|
||||
YASM_ARCH_CREATE_BAD_MACHINE, /**< Unrecognized machine name. */
|
||||
YASM_ARCH_CREATE_BAD_PARSER /**< Unrecognized parser name. */
|
||||
} yasm_arch_create_error;
|
||||
|
||||
/** Return values for yasm_arch_module::parse_check_insnprefix(). */
|
||||
typedef enum yasm_arch_insnprefix {
|
||||
YASM_ARCH_NOTINSNPREFIX = 0, /**< Unrecognized */
|
||||
YASM_ARCH_INSN, /**< An instruction */
|
||||
YASM_ARCH_PREFIX /**< An instruction prefix */
|
||||
} yasm_arch_insnprefix;
|
||||
|
||||
/** Types of registers / target modifiers that may be returned by
|
||||
* yasm_arch_module::parse_check_regtmod().
|
||||
*/
|
||||
typedef enum yasm_arch_regtmod {
|
||||
YASM_ARCH_NOTREGTMOD = 0, /**< Unrecognized */
|
||||
YASM_ARCH_REG, /**< A "normal" register */
|
||||
YASM_ARCH_REGGROUP, /**< A group of indexable registers */
|
||||
YASM_ARCH_SEGREG, /**< A segment register */
|
||||
YASM_ARCH_TARGETMOD /**< A target modifier (for jumps) */
|
||||
} yasm_arch_regtmod;
|
||||
|
||||
#ifndef YASM_DOXYGEN
|
||||
/** Base #yasm_arch structure. Must be present as the first element in any
|
||||
* #yasm_arch implementation.
|
||||
*/
|
||||
typedef struct yasm_arch_base {
|
||||
/** #yasm_arch_module implementation for this architecture. */
|
||||
const struct yasm_arch_module *module;
|
||||
} yasm_arch_base;
|
||||
#endif
|
||||
|
||||
/** YASM machine subtype. A number of different machine types may be
|
||||
* associated with a single architecture. These may be specific CPU's, but
|
||||
* the ABI used to interface with the architecture should be the primary
|
||||
* differentiator between machines. Some object formats (ELF) use the machine
|
||||
* to determine parameters within the generated output.
|
||||
*/
|
||||
typedef struct yasm_arch_machine {
|
||||
/** One-line description of the machine. */
|
||||
const char *name;
|
||||
|
||||
/** Keyword used to select machine. */
|
||||
const char *keyword;
|
||||
} yasm_arch_machine;
|
||||
|
||||
/** YASM architecture module interface.
|
||||
* \note All "data" in parser-related functions (yasm_arch_parse_*) needs to
|
||||
* start the parse initialized to 0 to make it okay for a parser-related
|
||||
* function to use/check previously stored data to see if it's been
|
||||
* called before on the same piece of data.
|
||||
*/
|
||||
typedef struct yasm_arch_module {
|
||||
/** One-line description of the architecture.
|
||||
* Call yasm_arch_name() to get the name of a particular #yasm_arch.
|
||||
*/
|
||||
const char *name;
|
||||
|
||||
/** Keyword used to select architecture.
|
||||
* Call yasm_arch_keyword() to get the keyword of a particular #yasm_arch.
|
||||
*/
|
||||
const char *keyword;
|
||||
|
||||
/** NULL-terminated list of directives. NULL if none. */
|
||||
/*@null@*/ const yasm_directive *directives;
|
||||
|
||||
/** Create architecture.
|
||||
* Module-level implementation of yasm_arch_create().
|
||||
* Call yasm_arch_create() instead of calling this function.
|
||||
*/
|
||||
/*@only@*/ yasm_arch * (*create) (const char *machine, const char *parser,
|
||||
/*@out@*/ yasm_arch_create_error *error);
|
||||
|
||||
/** Module-level implementation of yasm_arch_destroy().
|
||||
* Call yasm_arch_destroy() instead of calling this function.
|
||||
*/
|
||||
void (*destroy) (/*@only@*/ yasm_arch *arch);
|
||||
|
||||
/** Module-level implementation of yasm_arch_get_machine().
|
||||
* Call yasm_arch_get_machine() instead of calling this function.
|
||||
*/
|
||||
const char * (*get_machine) (const yasm_arch *arch);
|
||||
|
||||
/** Module-level implementation of yasm_arch_get_address_size().
|
||||
* Call yasm_arch_get_address_size() instead of calling this function.
|
||||
*/
|
||||
unsigned int (*get_address_size) (const yasm_arch *arch);
|
||||
|
||||
/** Module-level implementation of yasm_arch_set_var().
|
||||
* Call yasm_arch_set_var() instead of calling this function.
|
||||
*/
|
||||
int (*set_var) (yasm_arch *arch, const char *var, unsigned long val);
|
||||
|
||||
/** Module-level implementation of yasm_arch_parse_check_insnprefix().
|
||||
* Call yasm_arch_parse_check_insnprefix() instead of calling this function.
|
||||
*/
|
||||
yasm_arch_insnprefix (*parse_check_insnprefix)
|
||||
(yasm_arch *arch, const char *id, size_t id_len, unsigned long line,
|
||||
/*@out@*/ /*@only@*/ yasm_bytecode **bc, /*@out@*/ uintptr_t *prefix);
|
||||
|
||||
/** Module-level implementation of yasm_arch_parse_check_regtmod().
|
||||
* Call yasm_arch_parse_check_regtmod() instead of calling this function.
|
||||
*/
|
||||
yasm_arch_regtmod (*parse_check_regtmod)
|
||||
(yasm_arch *arch, const char *id, size_t id_len,
|
||||
/*@out@*/ uintptr_t *data);
|
||||
|
||||
/** Module-level implementation of yasm_arch_get_fill().
|
||||
* Call yasm_arch_get_fill() instead of calling this function.
|
||||
*/
|
||||
const unsigned char ** (*get_fill) (const yasm_arch *arch);
|
||||
|
||||
/** Module-level implementation of yasm_arch_floatnum_tobytes().
|
||||
* Call yasm_arch_floatnum_tobytes() instead of calling this function.
|
||||
*/
|
||||
int (*floatnum_tobytes) (yasm_arch *arch, const yasm_floatnum *flt,
|
||||
unsigned char *buf, size_t destsize,
|
||||
size_t valsize, size_t shift, int warn);
|
||||
|
||||
/** Module-level implementation of yasm_arch_intnum_tobytes().
|
||||
* Call yasm_arch_intnum_tobytes() instead of calling this function.
|
||||
*/
|
||||
int (*intnum_tobytes) (yasm_arch *arch, const yasm_intnum *intn,
|
||||
unsigned char *buf, size_t destsize, size_t valsize,
|
||||
int shift, const yasm_bytecode *bc,
|
||||
int warn);
|
||||
|
||||
/** Module-level implementation of yasm_arch_get_reg_size().
|
||||
* Call yasm_arch_get_reg_size() instead of calling this function.
|
||||
*/
|
||||
unsigned int (*get_reg_size) (yasm_arch *arch, uintptr_t reg);
|
||||
|
||||
/** Module-level implementation of yasm_arch_reggroup_get_reg().
|
||||
* Call yasm_arch_reggroup_get_reg() instead of calling this function.
|
||||
*/
|
||||
uintptr_t (*reggroup_get_reg) (yasm_arch *arch, uintptr_t reggroup,
|
||||
unsigned long regindex);
|
||||
|
||||
/** Module-level implementation of yasm_arch_reg_print().
|
||||
* Call yasm_arch_reg_print() instead of calling this function.
|
||||
*/
|
||||
void (*reg_print) (yasm_arch *arch, uintptr_t reg, FILE *f);
|
||||
|
||||
/** Module-level implementation of yasm_arch_segreg_print().
|
||||
* Call yasm_arch_segreg_print() instead of calling this function.
|
||||
*/
|
||||
void (*segreg_print) (yasm_arch *arch, uintptr_t segreg, FILE *f);
|
||||
|
||||
/** Module-level implementation of yasm_arch_ea_create().
|
||||
* Call yasm_arch_ea_create() instead of calling this function.
|
||||
*/
|
||||
yasm_effaddr * (*ea_create) (yasm_arch *arch, /*@keep@*/ yasm_expr *e);
|
||||
|
||||
/** Module-level implementation of yasm_arch_ea_destroy().
|
||||
* Call yasm_arch_ea_destroy() instead of calling this function.
|
||||
*/
|
||||
void (*ea_destroy) (/*@only@*/ yasm_effaddr *ea);
|
||||
|
||||
/** Module-level implementation of yasm_arch_ea_print().
|
||||
* Call yasm_arch_ea_print() instead of calling this function.
|
||||
*/
|
||||
void (*ea_print) (const yasm_effaddr *ea, FILE *f, int indent_level);
|
||||
|
||||
/** Module-level implementation of yasm_arch_create_empty_insn().
|
||||
* Call yasm_arch_create_empty_insn() instead of calling this function.
|
||||
*/
|
||||
/*@only@*/ yasm_bytecode * (*create_empty_insn) (yasm_arch *arch,
|
||||
unsigned long line);
|
||||
|
||||
/** NULL-terminated list of machines for this architecture.
|
||||
* Call yasm_arch_get_machine() to get the active machine of a particular
|
||||
* #yasm_arch.
|
||||
*/
|
||||
const yasm_arch_machine *machines;
|
||||
|
||||
/** Default machine keyword.
|
||||
* Call yasm_arch_get_machine() to get the active machine of a particular
|
||||
* #yasm_arch.
|
||||
*/
|
||||
const char *default_machine_keyword;
|
||||
|
||||
/** Canonical "word" size in bits.
|
||||
* Call yasm_arch_wordsize() to get the word size of a particular
|
||||
* #yasm_arch.
|
||||
*/
|
||||
unsigned int wordsize;
|
||||
|
||||
/** Worst case minimum instruction length in bytes.
|
||||
* Call yasm_arch_min_insn_len() to get the minimum instruction length of
|
||||
* a particular #yasm_arch.
|
||||
*/
|
||||
unsigned int min_insn_len;
|
||||
} yasm_arch_module;
|
||||
|
||||
/** Get the one-line description of an architecture.
|
||||
* \param arch architecture
|
||||
* \return One-line description of architecture.
|
||||
*/
|
||||
const char *yasm_arch_name(const yasm_arch *arch);
|
||||
|
||||
/** Get the keyword used to select an architecture.
|
||||
* \param arch architecture
|
||||
* \return Architecture keyword.
|
||||
*/
|
||||
const char *yasm_arch_keyword(const yasm_arch *arch);
|
||||
|
||||
/** Get the word size of an architecture.
|
||||
* \param arch architecture
|
||||
* \return Word size (in bits).
|
||||
*/
|
||||
unsigned int yasm_arch_wordsize(const yasm_arch *arch);
|
||||
|
||||
/** Get the minimum instruction length of an architecture.
|
||||
* \param arch architecture
|
||||
* \return Minimum instruction length (in bytes).
|
||||
*/
|
||||
unsigned int yasm_arch_min_insn_len(const yasm_arch *arch);
|
||||
|
||||
/** Create architecture.
|
||||
* \param module architecture module
|
||||
* \param machine keyword of machine in use (must be one listed in
|
||||
* #yasm_arch_module.machines)
|
||||
* \param parser keyword of parser in use
|
||||
* \param error error return value
|
||||
* \return NULL on error (error returned in error parameter), otherwise new
|
||||
* architecture.
|
||||
*/
|
||||
/*@only@*/ yasm_arch *yasm_arch_create(const yasm_arch_module *module,
|
||||
const char *machine, const char *parser,
|
||||
/*@out@*/ yasm_arch_create_error *error);
|
||||
|
||||
/** Clean up, free any architecture-allocated memory.
|
||||
* \param arch architecture
|
||||
*/
|
||||
void yasm_arch_destroy(/*@only@*/ yasm_arch *arch);
|
||||
|
||||
/** Get architecture's active machine name.
|
||||
* \param arch architecture
|
||||
* \return Active machine name.
|
||||
*/
|
||||
const char *yasm_arch_get_machine(const yasm_arch *arch);
|
||||
|
||||
/** Get architecture's active address size, in bits.
|
||||
* \param arch architecture
|
||||
* \return Active address size (in bits).
|
||||
*/
|
||||
unsigned int yasm_arch_get_address_size(const yasm_arch *arch);
|
||||
|
||||
/** Set any arch-specific variables. For example, "mode_bits" in x86.
|
||||
* \param arch architecture
|
||||
* \param var variable name
|
||||
* \param val value to set
|
||||
* \return Zero on success, non-zero on failure (variable does not exist).
|
||||
*/
|
||||
int yasm_arch_set_var(yasm_arch *arch, const char *var, unsigned long val);
|
||||
|
||||
/** Check an generic identifier to see if it matches architecture specific
|
||||
* names for instructions or instruction prefixes. Unrecognized identifiers
|
||||
* should return #YASM_ARCH_NOTINSNPREFIX so they can be treated as normal
|
||||
* symbols. Any additional data beyond just the type (almost always necessary)
|
||||
* should be returned into the space provided by the data parameter.
|
||||
* \param arch architecture
|
||||
* \param id identifier as in the input file
|
||||
* \param id_len length of id string
|
||||
* \param line virtual line
|
||||
* \param bc for instructions, yasm_insn-based bytecode is returned
|
||||
* (and NULL otherwise)
|
||||
* \param prefix for prefixes, yasm_arch-specific value is returned
|
||||
* (and 0 otherwise)
|
||||
* \return Identifier type (#YASM_ARCH_NOTINSNPREFIX if unrecognized)
|
||||
*/
|
||||
yasm_arch_insnprefix yasm_arch_parse_check_insnprefix
|
||||
(yasm_arch *arch, const char *id, size_t id_len, unsigned long line,
|
||||
/*@out@*/ /*@only@*/ yasm_bytecode **bc, /*@out@*/ uintptr_t *prefix);
|
||||
|
||||
/** Check an generic identifier to see if it matches architecture specific
|
||||
* names for registers or target modifiers. Unrecognized identifiers should
|
||||
* return #YASM_ARCH_NOTREGTMOD. Any additional data beyond just the type
|
||||
* (almost always necessary) should be returned into the space provided by the
|
||||
* data parameter.
|
||||
* \param arch architecture
|
||||
* \param id identifier as in the input file
|
||||
* \param id_len length of id string
|
||||
* \param data extra identification information (yasm_arch-specific)
|
||||
* [output]
|
||||
* \return Identifier type (#YASM_ARCH_NOTREGTMOD if unrecognized)
|
||||
*/
|
||||
yasm_arch_regtmod yasm_arch_parse_check_regtmod
|
||||
(yasm_arch *arch, const char *id, size_t id_len,
|
||||
/*@out@*/ uintptr_t *data);
|
||||
|
||||
/** Get NOP fill patterns for 1-15 bytes of fill.
|
||||
* \param arch architecture
|
||||
* \return 16-entry array of arrays; [0] is unused, [1] - [15] point to arrays
|
||||
* of 1-15 bytes (respectively) in length.
|
||||
*/
|
||||
const unsigned char **yasm_arch_get_fill(const yasm_arch *arch);
|
||||
|
||||
/** Output #yasm_floatnum to buffer. Puts the value into the least
|
||||
* significant bits of the destination, or may be shifted into more
|
||||
* significant bits by the shift parameter. The destination bits are
|
||||
* cleared before being set.
|
||||
* Architecture-specific because of endianness.
|
||||
* \param arch architecture
|
||||
* \param flt floating point value
|
||||
* \param buf buffer to write into
|
||||
* \param destsize destination size (in bytes)
|
||||
* \param valsize size (in bits)
|
||||
* \param shift left shift (in bits)
|
||||
* \param warn enables standard overflow/underflow warnings
|
||||
* \return Nonzero on error.
|
||||
*/
|
||||
int yasm_arch_floatnum_tobytes(yasm_arch *arch, const yasm_floatnum *flt,
|
||||
unsigned char *buf, size_t destsize,
|
||||
size_t valsize, size_t shift, int warn);
|
||||
|
||||
/** Output #yasm_intnum to buffer. Puts the value into the least
|
||||
* significant bits of the destination, or may be shifted into more
|
||||
* significant bits by the shift parameter. The destination bits are
|
||||
* cleared before being set.
|
||||
* \param arch architecture
|
||||
* \param intn integer value
|
||||
* \param buf buffer to write into
|
||||
* \param destsize destination size (in bytes)
|
||||
* \param valsize size (in bits)
|
||||
* \param shift left shift (in bits); may be negative to specify right
|
||||
* shift (standard warnings include truncation to boundary)
|
||||
* \param bc bytecode being output ("parent" of value)
|
||||
* \param warn enables standard warnings (value doesn't fit into
|
||||
* valsize bits)
|
||||
* \return Nonzero on error.
|
||||
*/
|
||||
int yasm_arch_intnum_tobytes(yasm_arch *arch, const yasm_intnum *intn,
|
||||
unsigned char *buf, size_t destsize,
|
||||
size_t valsize, int shift,
|
||||
const yasm_bytecode *bc, int warn);
|
||||
|
||||
/** Get the equivalent size of a register in bits.
|
||||
* \param arch architecture
|
||||
* \param reg register
|
||||
* \return 0 if there is no suitable equivalent size, otherwise the size.
|
||||
*/
|
||||
unsigned int yasm_arch_get_reg_size(yasm_arch *arch, uintptr_t reg);
|
||||
|
||||
/** Get a specific register of a register group, based on the register
|
||||
* group and the index within the group.
|
||||
* \param arch architecture
|
||||
* \param reggroup register group
|
||||
* \param regindex register index
|
||||
* \return 0 if regindex is not valid for that register group, otherwise the
|
||||
* specific register value.
|
||||
*/
|
||||
uintptr_t yasm_arch_reggroup_get_reg(yasm_arch *arch, uintptr_t reggroup,
|
||||
unsigned long regindex);
|
||||
|
||||
/** Print a register. For debugging purposes.
|
||||
* \param arch architecture
|
||||
* \param reg register
|
||||
* \param f file
|
||||
*/
|
||||
void yasm_arch_reg_print(yasm_arch *arch, uintptr_t reg, FILE *f);
|
||||
|
||||
/** Print a segment register. For debugging purposes.
|
||||
* \param arch architecture
|
||||
* \param segreg segment register
|
||||
* \param f file
|
||||
*/
|
||||
void yasm_arch_segreg_print(yasm_arch *arch, uintptr_t segreg, FILE *f);
|
||||
|
||||
/** Create an effective address from an expression.
|
||||
* \param arch architecture
|
||||
* \param e expression (kept, do not delete)
|
||||
* \return Newly allocated effective address.
|
||||
*/
|
||||
yasm_effaddr *yasm_arch_ea_create(yasm_arch *arch, /*@keep@*/ yasm_expr *e);
|
||||
|
||||
/** Delete (free allocated memory for) an effective address.
|
||||
* \param arch architecture
|
||||
* \param ea effective address (only pointer to it).
|
||||
*/
|
||||
void yasm_arch_ea_destroy(yasm_arch *arch, /*@only@*/ yasm_effaddr *ea);
|
||||
|
||||
/** Print an effective address. For debugging purposes.
|
||||
* \param arch architecture
|
||||
* \param ea effective address
|
||||
* \param f file
|
||||
* \param indent_level indentation level
|
||||
*/
|
||||
void yasm_arch_ea_print(const yasm_arch *arch, const yasm_effaddr *ea,
|
||||
FILE *f, int indent_level);
|
||||
|
||||
/** Create a bytecode that represents a single empty (0 length) instruction.
|
||||
* This is used for handling solitary prefixes.
|
||||
* \param arch architecture
|
||||
* \param line virtual line (from yasm_linemap)
|
||||
* \return Newly allocated bytecode.
|
||||
*/
|
||||
/*@only@*/ yasm_bytecode *yasm_arch_create_empty_insn(yasm_arch *arch,
|
||||
unsigned long line);
|
||||
|
||||
#ifndef YASM_DOXYGEN
|
||||
|
||||
/* Inline macro implementations for arch functions */
|
||||
|
||||
#define yasm_arch_name(arch) \
|
||||
(((yasm_arch_base *)arch)->module->name)
|
||||
#define yasm_arch_keyword(arch) \
|
||||
(((yasm_arch_base *)arch)->module->keyword)
|
||||
#define yasm_arch_wordsize(arch) \
|
||||
(((yasm_arch_base *)arch)->module->wordsize)
|
||||
#define yasm_arch_min_insn_len(arch) \
|
||||
(((yasm_arch_base *)arch)->module->min_insn_len)
|
||||
|
||||
#define yasm_arch_create(module, machine, parser, error) \
|
||||
module->create(machine, parser, error)
|
||||
|
||||
#define yasm_arch_destroy(arch) \
|
||||
((yasm_arch_base *)arch)->module->destroy(arch)
|
||||
#define yasm_arch_get_machine(arch) \
|
||||
((yasm_arch_base *)arch)->module->get_machine(arch)
|
||||
#define yasm_arch_get_address_size(arch) \
|
||||
((yasm_arch_base *)arch)->module->get_address_size(arch)
|
||||
#define yasm_arch_set_var(arch, var, val) \
|
||||
((yasm_arch_base *)arch)->module->set_var(arch, var, val)
|
||||
#define yasm_arch_parse_check_insnprefix(arch, id, id_len, line, bc, prefix) \
|
||||
((yasm_arch_base *)arch)->module->parse_check_insnprefix \
|
||||
(arch, id, id_len, line, bc, prefix)
|
||||
#define yasm_arch_parse_check_regtmod(arch, id, id_len, data) \
|
||||
((yasm_arch_base *)arch)->module->parse_check_regtmod \
|
||||
(arch, id, id_len, data)
|
||||
#define yasm_arch_get_fill(arch) \
|
||||
((yasm_arch_base *)arch)->module->get_fill(arch)
|
||||
#define yasm_arch_floatnum_tobytes(arch, flt, buf, destsize, valsize, shift, \
|
||||
warn) \
|
||||
((yasm_arch_base *)arch)->module->floatnum_tobytes \
|
||||
(arch, flt, buf, destsize, valsize, shift, warn)
|
||||
#define yasm_arch_intnum_tobytes(arch, intn, buf, destsize, valsize, shift, \
|
||||
bc, warn) \
|
||||
((yasm_arch_base *)arch)->module->intnum_tobytes \
|
||||
(arch, intn, buf, destsize, valsize, shift, bc, warn)
|
||||
#define yasm_arch_get_reg_size(arch, reg) \
|
||||
((yasm_arch_base *)arch)->module->get_reg_size(arch, reg)
|
||||
#define yasm_arch_reggroup_get_reg(arch, regg, regi) \
|
||||
((yasm_arch_base *)arch)->module->reggroup_get_reg(arch, regg, regi)
|
||||
#define yasm_arch_reg_print(arch, reg, f) \
|
||||
((yasm_arch_base *)arch)->module->reg_print(arch, reg, f)
|
||||
#define yasm_arch_segreg_print(arch, segreg, f) \
|
||||
((yasm_arch_base *)arch)->module->segreg_print(arch, segreg, f)
|
||||
#define yasm_arch_ea_create(arch, e) \
|
||||
((yasm_arch_base *)arch)->module->ea_create(arch, e)
|
||||
#define yasm_arch_ea_destroy(arch, ea) \
|
||||
((yasm_arch_base *)arch)->module->ea_destroy(ea)
|
||||
#define yasm_arch_ea_print(arch, ea, f, i) \
|
||||
((yasm_arch_base *)arch)->module->ea_print(ea, f, i)
|
||||
#define yasm_arch_create_empty_insn(arch, line) \
|
||||
((yasm_arch_base *)arch)->module->create_empty_insn(arch, line)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* \file assocdat.h
|
||||
* \brief YASM associated data storage (libyasm internal use)
|
||||
*
|
||||
* \license
|
||||
* Copyright (C) 2003-2007 Peter Johnson
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* \endlicense
|
||||
*/
|
||||
#ifndef YASM_ASSOCDAT_H
|
||||
#define YASM_ASSOCDAT_H
|
||||
|
||||
#ifndef YASM_LIB_DECL
|
||||
#define YASM_LIB_DECL
|
||||
#endif
|
||||
|
||||
/** Associated data container. */
|
||||
typedef struct yasm__assoc_data yasm__assoc_data;
|
||||
|
||||
/** Create an associated data container. */
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm__assoc_data *yasm__assoc_data_create(void);
|
||||
|
||||
/** Get associated data for a data callback.
|
||||
* \param assoc_data container of associated data
|
||||
* \param callback callback used when adding data
|
||||
* \return Associated data (NULL if none).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@dependent@*/ /*@null@*/ void *yasm__assoc_data_get
|
||||
(/*@null@*/ yasm__assoc_data *assoc_data,
|
||||
const yasm_assoc_data_callback *callback);
|
||||
|
||||
/** Add associated data to a associated data container.
|
||||
* \attention Deletes any existing associated data for that data callback.
|
||||
* \param assoc_data container of associated data
|
||||
* \param callback callback
|
||||
* \param data data to associate
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm__assoc_data *yasm__assoc_data_add
|
||||
(/*@null@*/ /*@only@*/ yasm__assoc_data *assoc_data,
|
||||
const yasm_assoc_data_callback *callback,
|
||||
/*@only@*/ /*@null@*/ void *data);
|
||||
|
||||
/** Destroy all associated data in a container. */
|
||||
YASM_LIB_DECL
|
||||
void yasm__assoc_data_destroy
|
||||
(/*@null@*/ /*@only@*/ yasm__assoc_data *assoc_data);
|
||||
|
||||
/** Print all associated data in a container. */
|
||||
YASM_LIB_DECL
|
||||
void yasm__assoc_data_print(const yasm__assoc_data *assoc_data, FILE *f,
|
||||
int indent_level);
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,666 @@
|
||||
#ifndef YASM_BITVECT_H
|
||||
#define YASM_BITVECT_H
|
||||
/*****************************************************************************/
|
||||
/* MODULE NAME: BitVector.h MODULE TYPE: (adt) */
|
||||
/*****************************************************************************/
|
||||
/* MODULE IMPORTS: */
|
||||
/*****************************************************************************/
|
||||
|
||||
/* ToolBox.h */
|
||||
/*****************************************************************************/
|
||||
/* NOTE: The type names that have been chosen here are somewhat weird on */
|
||||
/* purpose, in order to avoid name clashes with system header files */
|
||||
/* and your own application(s) which might - directly or indirectly - */
|
||||
/* include this definitions file. */
|
||||
/*****************************************************************************/
|
||||
#ifndef YASM_LIB_DECL
|
||||
#define YASM_LIB_DECL
|
||||
#endif
|
||||
|
||||
typedef unsigned char N_char;
|
||||
typedef unsigned char N_byte;
|
||||
typedef unsigned short N_short;
|
||||
typedef unsigned short N_shortword;
|
||||
typedef unsigned int N_int;
|
||||
typedef unsigned int N_word;
|
||||
typedef unsigned long N_long;
|
||||
typedef unsigned long N_longword;
|
||||
|
||||
/* Mnemonic 1: The natural numbers, N = { 0, 1, 2, 3, ... } */
|
||||
/* Mnemonic 2: Nnnn = u_N_signed, _N_ot signed */
|
||||
|
||||
typedef signed char Z_char;
|
||||
typedef signed char Z_byte;
|
||||
typedef signed short Z_short;
|
||||
typedef signed short Z_shortword;
|
||||
typedef signed int Z_int;
|
||||
typedef signed int Z_word;
|
||||
typedef signed long Z_long;
|
||||
typedef signed long Z_longword;
|
||||
|
||||
/* Mnemonic 1: The whole numbers, Z = { 0, -1, 1, -2, 2, -3, 3, ... } */
|
||||
/* Mnemonic 2: Zzzz = Ssss_igned */
|
||||
|
||||
typedef void *voidptr;
|
||||
typedef N_char *charptr;
|
||||
typedef N_byte *byteptr;
|
||||
typedef N_short *shortptr;
|
||||
typedef N_shortword *shortwordptr;
|
||||
typedef N_int *intptr;
|
||||
typedef N_word *wordptr;
|
||||
typedef N_long *longptr;
|
||||
typedef N_longword *longwordptr;
|
||||
|
||||
typedef N_char *N_charptr;
|
||||
typedef N_byte *N_byteptr;
|
||||
typedef N_short *N_shortptr;
|
||||
typedef N_shortword *N_shortwordptr;
|
||||
typedef N_int *N_intptr;
|
||||
typedef N_word *N_wordptr;
|
||||
typedef N_long *N_longptr;
|
||||
typedef N_longword *N_longwordptr;
|
||||
|
||||
typedef Z_char *Z_charptr;
|
||||
typedef Z_byte *Z_byteptr;
|
||||
typedef Z_short *Z_shortptr;
|
||||
typedef Z_shortword *Z_shortwordptr;
|
||||
typedef Z_int *Z_intptr;
|
||||
typedef Z_word *Z_wordptr;
|
||||
typedef Z_long *Z_longptr;
|
||||
typedef Z_longword *Z_longwordptr;
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE (0!=0)
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE (0==0)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
typedef bool boolean;
|
||||
#else
|
||||
#ifdef MACOS_TRADITIONAL
|
||||
#define boolean Boolean
|
||||
#else
|
||||
typedef enum boolean { false = FALSE, true = TRUE } boolean;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/* MODULE INTERFACE: */
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef enum ErrCode
|
||||
{
|
||||
ErrCode_Ok = 0, /* everything went allright */
|
||||
|
||||
ErrCode_Type, /* types word and size_t have incompatible sizes */
|
||||
ErrCode_Bits, /* bits of word and sizeof(word) are inconsistent */
|
||||
ErrCode_Word, /* size of word is less than 16 bits */
|
||||
ErrCode_Long, /* size of word is greater than size of long */
|
||||
ErrCode_Powr, /* number of bits of word is not a power of two */
|
||||
ErrCode_Loga, /* error in calculation of logarithm */
|
||||
|
||||
ErrCode_Null, /* unable to allocate memory */
|
||||
|
||||
ErrCode_Indx, /* index out of range */
|
||||
ErrCode_Ordr, /* minimum > maximum index */
|
||||
ErrCode_Size, /* bit vector size mismatch */
|
||||
ErrCode_Pars, /* input string syntax error */
|
||||
ErrCode_Ovfl, /* numeric overflow error */
|
||||
ErrCode_Same, /* operands must be distinct */
|
||||
ErrCode_Expo, /* exponent must be positive */
|
||||
ErrCode_Zero /* division by zero error */
|
||||
} ErrCode;
|
||||
|
||||
typedef wordptr *listptr;
|
||||
|
||||
/* ===> MISCELLANEOUS BASIC FUNCTIONS: <=== */
|
||||
|
||||
YASM_LIB_DECL
|
||||
const char * BitVector_Error (ErrCode error); /* return string for err code */
|
||||
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_Boot (void); /* 0 = ok, 1..7 = error */
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Shutdown (void); /* undo Boot */
|
||||
|
||||
YASM_LIB_DECL
|
||||
N_word BitVector_Size (N_int bits); /* bit vector size (# of words) */
|
||||
YASM_LIB_DECL
|
||||
N_word BitVector_Mask (N_int bits); /* bit vector mask (unused bits) */
|
||||
|
||||
/* ===> CLASS METHODS: <=== */
|
||||
|
||||
YASM_LIB_DECL
|
||||
const char * BitVector_Version (void); /* returns version string */
|
||||
|
||||
YASM_LIB_DECL
|
||||
N_int BitVector_Word_Bits (void); /* return # of bits in machine word */
|
||||
YASM_LIB_DECL
|
||||
N_int BitVector_Long_Bits (void); /* return # of bits in unsigned long */
|
||||
|
||||
/* ===> CONSTRUCTOR METHODS: <=== */
|
||||
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ wordptr BitVector_Create (N_int bits, boolean clear); /* malloc */
|
||||
YASM_LIB_DECL
|
||||
listptr BitVector_Create_List(N_int bits, boolean clear, N_int count);
|
||||
|
||||
YASM_LIB_DECL
|
||||
wordptr BitVector_Resize (wordptr oldaddr, N_int bits); /* realloc */
|
||||
|
||||
YASM_LIB_DECL
|
||||
wordptr BitVector_Shadow (wordptr addr); /* make new same size but empty */
|
||||
YASM_LIB_DECL
|
||||
wordptr BitVector_Clone (wordptr addr); /* make exact duplicate */
|
||||
|
||||
YASM_LIB_DECL
|
||||
wordptr BitVector_Concat (wordptr X, wordptr Y); /* return concatenation */
|
||||
|
||||
/* ===> DESTRUCTOR METHODS: <=== */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Dispose (/*@only@*/ /*@out@*/ charptr string); /* string */
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Destroy (/*@only@*/ wordptr addr); /* bitvec */
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Destroy_List (listptr list, N_int count); /* list */
|
||||
|
||||
/* ===> OBJECT METHODS: <=== */
|
||||
|
||||
/* ===> bit vector copy function: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Copy (wordptr X, wordptr Y); /* X = Y */
|
||||
|
||||
/* ===> bit vector initialization: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Empty (wordptr addr); /* X = {} */
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Fill (wordptr addr); /* X = ~{} */
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Flip (wordptr addr); /* X = ~X */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Primes (wordptr addr);
|
||||
|
||||
/* ===> miscellaneous functions: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Reverse (wordptr X, wordptr Y);
|
||||
|
||||
/* ===> bit vector interval operations and functions: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Interval_Empty (/*@out@*/ wordptr addr, N_int lower, N_int upper);
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Interval_Fill (/*@out@*/ wordptr addr, N_int lower, N_int upper);
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Interval_Flip (/*@out@*/ wordptr addr, N_int lower, N_int upper);
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Interval_Reverse (/*@out@*/ wordptr addr, N_int lower, N_int upper);
|
||||
|
||||
YASM_LIB_DECL
|
||||
boolean BitVector_interval_scan_inc (wordptr addr, N_int start,
|
||||
N_intptr min, N_intptr max);
|
||||
YASM_LIB_DECL
|
||||
boolean BitVector_interval_scan_dec (wordptr addr, N_int start,
|
||||
N_intptr min, N_intptr max);
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Interval_Copy (/*@out@*/ wordptr X, wordptr Y, N_int Xoffset,
|
||||
N_int Yoffset, N_int length);
|
||||
|
||||
YASM_LIB_DECL
|
||||
wordptr BitVector_Interval_Substitute(/*@out@*/ wordptr X, wordptr Y,
|
||||
N_int Xoffset, N_int Xlength,
|
||||
N_int Yoffset, N_int Ylength);
|
||||
|
||||
/* ===> bit vector test functions: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
boolean BitVector_is_empty (wordptr addr); /* X == {} ? */
|
||||
YASM_LIB_DECL
|
||||
boolean BitVector_is_full (wordptr addr); /* X == ~{} ? */
|
||||
|
||||
YASM_LIB_DECL
|
||||
boolean BitVector_equal (wordptr X, wordptr Y); /* X == Y ? */
|
||||
YASM_LIB_DECL
|
||||
Z_int BitVector_Lexicompare(wordptr X, wordptr Y); /* X <,=,> Y ? */
|
||||
YASM_LIB_DECL
|
||||
Z_int BitVector_Compare (wordptr X, wordptr Y); /* X <,=,> Y ? */
|
||||
|
||||
/* ===> bit vector string conversion functions: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ charptr BitVector_to_Hex (wordptr addr);
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_from_Hex (/*@out@*/wordptr addr, charptr string);
|
||||
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_from_Oct(/*@out@*/ wordptr addr, charptr string);
|
||||
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ charptr BitVector_to_Bin (wordptr addr);
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_from_Bin (/*@out@*/ wordptr addr, charptr string);
|
||||
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ charptr BitVector_to_Dec (wordptr addr);
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_from_Dec (/*@out@*/ wordptr addr, charptr string);
|
||||
|
||||
typedef struct BitVector_from_Dec_static_data BitVector_from_Dec_static_data;
|
||||
YASM_LIB_DECL
|
||||
BitVector_from_Dec_static_data *BitVector_from_Dec_static_Boot(N_word bits);
|
||||
YASM_LIB_DECL
|
||||
void BitVector_from_Dec_static_Shutdown(/*@null@*/ BitVector_from_Dec_static_data *data);
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_from_Dec_static(BitVector_from_Dec_static_data *data,
|
||||
/*@out@*/ wordptr addr, charptr string);
|
||||
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ charptr BitVector_to_Enum (wordptr addr);
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_from_Enum (/*@out@*/ wordptr addr, charptr string);
|
||||
|
||||
/* ===> bit vector bit operations, functions & tests: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Bit_Off (/*@out@*/ wordptr addr, N_int indx); /* X = X \ {x} */
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Bit_On (/*@out@*/ wordptr addr, N_int indx); /* X = X + {x} */
|
||||
YASM_LIB_DECL
|
||||
boolean BitVector_bit_flip (/*@out@*/ wordptr addr, N_int indx); /* (X+{x})\(X*{x}) */
|
||||
|
||||
YASM_LIB_DECL
|
||||
boolean BitVector_bit_test (wordptr addr, N_int indx); /* {x} in X ? */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Bit_Copy (/*@out@*/ wordptr addr, N_int indx, boolean bit);
|
||||
|
||||
/* ===> bit vector bit shift & rotate functions: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_LSB (/*@out@*/ wordptr addr, boolean bit);
|
||||
YASM_LIB_DECL
|
||||
void BitVector_MSB (/*@out@*/ wordptr addr, boolean bit);
|
||||
YASM_LIB_DECL
|
||||
boolean BitVector_lsb_ (wordptr addr);
|
||||
YASM_LIB_DECL
|
||||
boolean BitVector_msb_ (wordptr addr);
|
||||
YASM_LIB_DECL
|
||||
boolean /*@alt void@*/ BitVector_rotate_left (wordptr addr);
|
||||
YASM_LIB_DECL
|
||||
boolean /*@alt void@*/ BitVector_rotate_right (wordptr addr);
|
||||
YASM_LIB_DECL
|
||||
boolean /*@alt void@*/ BitVector_shift_left (wordptr addr, boolean carry_in);
|
||||
YASM_LIB_DECL
|
||||
boolean /*@alt void@*/ BitVector_shift_right (wordptr addr, boolean carry_in);
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Move_Left (wordptr addr, N_int bits);
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Move_Right (wordptr addr, N_int bits);
|
||||
|
||||
/* ===> bit vector insert/delete bits: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Insert (wordptr addr, N_int offset, N_int count,
|
||||
boolean clear);
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Delete (wordptr addr, N_int offset, N_int count,
|
||||
boolean clear);
|
||||
|
||||
/* ===> bit vector arithmetic: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
boolean /*@alt void@*/ BitVector_increment (wordptr addr); /* X++ */
|
||||
YASM_LIB_DECL
|
||||
boolean /*@alt void@*/ BitVector_decrement (wordptr addr); /* X-- */
|
||||
|
||||
YASM_LIB_DECL
|
||||
boolean /*@alt void@*/ BitVector_compute (wordptr X, wordptr Y, wordptr Z, boolean minus,
|
||||
boolean *carry);
|
||||
YASM_LIB_DECL
|
||||
boolean /*@alt void@*/ BitVector_add (wordptr X, wordptr Y, wordptr Z, boolean *carry);
|
||||
YASM_LIB_DECL
|
||||
boolean /*@alt void@*/ BitVector_sub (wordptr X, wordptr Y, wordptr Z, boolean *carry);
|
||||
YASM_LIB_DECL
|
||||
boolean /*@alt void@*/ BitVector_inc (wordptr X, wordptr Y);
|
||||
YASM_LIB_DECL
|
||||
boolean /*@alt void@*/ BitVector_dec (wordptr X, wordptr Y);
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Negate (wordptr X, wordptr Y);
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Absolute (wordptr X, wordptr Y);
|
||||
YASM_LIB_DECL
|
||||
Z_int BitVector_Sign (wordptr addr);
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_Mul_Pos (wordptr X, wordptr Y, wordptr Z, boolean strict);
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_Multiply (wordptr X, wordptr Y, wordptr Z);
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_Div_Pos (wordptr Q, wordptr X, wordptr Y, wordptr R);
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_Divide (wordptr Q, wordptr X, wordptr Y, wordptr R);
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_GCD (wordptr X, wordptr Y, wordptr Z);
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_GCD2 (wordptr U, wordptr V, wordptr W, /* O */
|
||||
wordptr X, wordptr Y); /* I */
|
||||
YASM_LIB_DECL
|
||||
ErrCode BitVector_Power (wordptr X, wordptr Y, wordptr Z);
|
||||
|
||||
/* ===> direct memory access functions: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Block_Store(wordptr addr, charptr buffer, N_int length);
|
||||
YASM_LIB_DECL
|
||||
charptr BitVector_Block_Read (wordptr addr, /*@out@*/ N_intptr length);
|
||||
|
||||
/* ===> word array functions: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Word_Store (wordptr addr, N_int offset, N_int value);
|
||||
YASM_LIB_DECL
|
||||
N_int BitVector_Word_Read (wordptr addr, N_int offset);
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Word_Insert(wordptr addr, N_int offset, N_int count,
|
||||
boolean clear);
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Word_Delete(wordptr addr, N_int offset, N_int count,
|
||||
boolean clear);
|
||||
|
||||
/* ===> arbitrary size chunk functions: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void BitVector_Chunk_Store(wordptr addr, N_int chunksize,
|
||||
N_int offset, N_long value);
|
||||
YASM_LIB_DECL
|
||||
N_long BitVector_Chunk_Read (wordptr addr, N_int chunksize,
|
||||
N_int offset);
|
||||
|
||||
/* ===> set operations: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void Set_Union (wordptr X, wordptr Y, wordptr Z); /* X = Y + Z */
|
||||
YASM_LIB_DECL
|
||||
void Set_Intersection (wordptr X, wordptr Y, wordptr Z); /* X = Y * Z */
|
||||
YASM_LIB_DECL
|
||||
void Set_Difference (wordptr X, wordptr Y, wordptr Z); /* X = Y \ Z */
|
||||
YASM_LIB_DECL
|
||||
void Set_ExclusiveOr (wordptr X, wordptr Y, wordptr Z); /*(Y+Z)\(Y*Z)*/
|
||||
YASM_LIB_DECL
|
||||
void Set_Complement (wordptr X, wordptr Y); /* X = ~Y */
|
||||
|
||||
/* ===> set functions: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
boolean Set_subset (wordptr X, wordptr Y); /* X in Y ? */
|
||||
|
||||
YASM_LIB_DECL
|
||||
N_int Set_Norm (wordptr addr); /* = | X | */
|
||||
YASM_LIB_DECL
|
||||
N_int Set_Norm2 (wordptr addr); /* = | X | */
|
||||
YASM_LIB_DECL
|
||||
N_int Set_Norm3 (wordptr addr); /* = | X | */
|
||||
YASM_LIB_DECL
|
||||
Z_long Set_Min (wordptr addr); /* = min(X) */
|
||||
YASM_LIB_DECL
|
||||
Z_long Set_Max (wordptr addr); /* = max(X) */
|
||||
|
||||
/* ===> matrix-of-booleans operations: */
|
||||
|
||||
YASM_LIB_DECL
|
||||
void Matrix_Multiplication(wordptr X, N_int rowsX, N_int colsX,
|
||||
wordptr Y, N_int rowsY, N_int colsY,
|
||||
wordptr Z, N_int rowsZ, N_int colsZ);
|
||||
|
||||
YASM_LIB_DECL
|
||||
void Matrix_Product (wordptr X, N_int rowsX, N_int colsX,
|
||||
wordptr Y, N_int rowsY, N_int colsY,
|
||||
wordptr Z, N_int rowsZ, N_int colsZ);
|
||||
|
||||
YASM_LIB_DECL
|
||||
void Matrix_Closure (wordptr addr, N_int rows, N_int cols);
|
||||
|
||||
YASM_LIB_DECL
|
||||
void Matrix_Transpose (wordptr X, N_int rowsX, N_int colsX,
|
||||
wordptr Y, N_int rowsY, N_int colsY);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* VERSION: 6.4 */
|
||||
/*****************************************************************************/
|
||||
/* VERSION HISTORY: */
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* Version 6.4 03.10.04 Added C++ comp. directives. Improved "Norm()". */
|
||||
/* Version 6.3 28.09.02 Added "Create_List()" and "GCD2()". */
|
||||
/* Version 6.2 15.09.02 Overhauled error handling. Fixed "GCD()". */
|
||||
/* Version 6.1 08.10.01 Make VMS linker happy: _lsb,_msb => _lsb_,_msb_ */
|
||||
/* Version 6.0 08.10.00 Corrected overflow handling. */
|
||||
/* Version 5.8 14.07.00 Added "Power()". Changed "Copy()". */
|
||||
/* Version 5.7 19.05.99 Quickened "Div_Pos()". Added "Product()". */
|
||||
/* Version 5.6 02.11.98 Leading zeros eliminated in "to_Hex()". */
|
||||
/* Version 5.5 21.09.98 Fixed bug of uninitialized "error" in Multiply. */
|
||||
/* Version 5.4 07.09.98 Fixed bug of uninitialized "error" in Divide. */
|
||||
/* Version 5.3 12.05.98 Improved Norm. Completed history. */
|
||||
/* Version 5.2 31.03.98 Improved Norm. */
|
||||
/* Version 5.1 09.03.98 No changes. */
|
||||
/* Version 5.0 01.03.98 Major additions and rewrite. */
|
||||
/* Version 4.2 16.07.97 Added is_empty, is_full. */
|
||||
/* Version 4.1 30.06.97 Added word-ins/del, move-left/right, inc/dec. */
|
||||
/* Version 4.0 23.04.97 Rewrite. Added bit shift and bool. matrix ops. */
|
||||
/* Version 3.2 04.02.97 Added interval methods. */
|
||||
/* Version 3.1 21.01.97 Fixed bug on 64 bit machines. */
|
||||
/* Version 3.0 12.01.97 Added flip. */
|
||||
/* Version 2.0 14.12.96 Efficiency and consistency improvements. */
|
||||
/* Version 1.1 08.01.96 Added Resize and ExclusiveOr. */
|
||||
/* Version 1.0 14.12.95 First version under UNIX (with Perl module). */
|
||||
/* Version 0.9 01.11.93 First version of C library under MS-DOS. */
|
||||
/* Version 0.1 ??.??.89 First version in Turbo Pascal under CP/M. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
/* AUTHOR: */
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* Steffen Beyer */
|
||||
/* mailto:sb@engelschall.com */
|
||||
/* http://www.engelschall.com/u/sb/download/ */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
/* COPYRIGHT: */
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* Copyright (c) 1995 - 2004 by Steffen Beyer. */
|
||||
/* All rights reserved. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
/* LICENSE: */
|
||||
/*****************************************************************************/
|
||||
/* This package is free software; you can use, modify and redistribute */
|
||||
/* it under the same terms as Perl itself, i.e., under the terms of */
|
||||
/* the "Artistic License" or the "GNU General Public License". */
|
||||
/* */
|
||||
/* The C library at the core of this Perl module can additionally */
|
||||
/* be used, modified and redistributed under the terms of the */
|
||||
/* "GNU Library General Public License". */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
/* ARTISTIC LICENSE: */
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
The "Artistic License"
|
||||
|
||||
Preamble
|
||||
|
||||
The intent of this document is to state the conditions under which a
|
||||
Package may be copied, such that the Copyright Holder maintains some
|
||||
semblance of artistic control over the development of the package,
|
||||
while giving the users of the package the right to use and distribute
|
||||
the Package in a more-or-less customary fashion, plus the right to make
|
||||
reasonable modifications.
|
||||
|
||||
Definitions:
|
||||
|
||||
"Package" refers to the collection of files distributed by the
|
||||
Copyright Holder, and derivatives of that collection of files
|
||||
created through textual modification.
|
||||
|
||||
"Standard Version" refers to such a Package if it has not been
|
||||
modified, or has been modified in accordance with the wishes
|
||||
of the Copyright Holder as specified below.
|
||||
|
||||
"Copyright Holder" is whoever is named in the copyright or
|
||||
copyrights for the package.
|
||||
|
||||
"You" is you, if you're thinking about copying or distributing
|
||||
this Package.
|
||||
|
||||
"Reasonable copying fee" is whatever you can justify on the
|
||||
basis of media cost, duplication charges, time of people involved,
|
||||
and so on. (You will not be required to justify it to the
|
||||
Copyright Holder, but only to the computing community at large
|
||||
as a market that must bear the fee.)
|
||||
|
||||
"Freely Available" means that no fee is charged for the item
|
||||
itself, though there may be fees involved in handling the item.
|
||||
It also means that recipients of the item may redistribute it
|
||||
under the same conditions they received it.
|
||||
|
||||
1. You may make and give away verbatim copies of the source form of the
|
||||
Standard Version of this Package without restriction, provided that you
|
||||
duplicate all of the original copyright notices and associated disclaimers.
|
||||
|
||||
2. You may apply bug fixes, portability fixes and other modifications
|
||||
derived from the Public Domain or from the Copyright Holder. A Package
|
||||
modified in such a way shall still be considered the Standard Version.
|
||||
|
||||
3. You may otherwise modify your copy of this Package in any way, provided
|
||||
that you insert a prominent notice in each changed file stating how and
|
||||
when you changed that file, and provided that you do at least ONE of the
|
||||
following:
|
||||
|
||||
a) place your modifications in the Public Domain or otherwise make them
|
||||
Freely Available, such as by posting said modifications to Usenet or
|
||||
an equivalent medium, or placing the modifications on a major archive
|
||||
site such as uunet.uu.net, or by allowing the Copyright Holder to include
|
||||
your modifications in the Standard Version of the Package.
|
||||
|
||||
b) use the modified Package only within your corporation or organization.
|
||||
|
||||
c) rename any non-standard executables so the names do not conflict
|
||||
with standard executables, which must also be provided, and provide
|
||||
a separate manual page for each non-standard executable that clearly
|
||||
documents how it differs from the Standard Version.
|
||||
|
||||
d) make other distribution arrangements with the Copyright Holder.
|
||||
|
||||
4. You may distribute the programs of this Package in object code or
|
||||
executable form, provided that you do at least ONE of the following:
|
||||
|
||||
a) distribute a Standard Version of the executables and library files,
|
||||
together with instructions (in the manual page or equivalent) on where
|
||||
to get the Standard Version.
|
||||
|
||||
b) accompany the distribution with the machine-readable source of
|
||||
the Package with your modifications.
|
||||
|
||||
c) give non-standard executables non-standard names, and clearly
|
||||
document the differences in manual pages (or equivalent), together
|
||||
with instructions on where to get the Standard Version.
|
||||
|
||||
d) make other distribution arrangements with the Copyright Holder.
|
||||
|
||||
5. You may charge a reasonable copying fee for any distribution of this
|
||||
Package. You may charge any fee you choose for support of this
|
||||
Package. You may not charge a fee for this Package itself. However,
|
||||
you may distribute this Package in aggregate with other (possibly
|
||||
commercial) programs as part of a larger (possibly commercial) software
|
||||
distribution provided that you do not advertise this Package as a
|
||||
product of your own. You may embed this Package's interpreter within
|
||||
an executable of yours (by linking); this shall be construed as a mere
|
||||
form of aggregation, provided that the complete Standard Version of the
|
||||
interpreter is so embedded.
|
||||
|
||||
6. The scripts and library files supplied as input to or produced as
|
||||
output from the programs of this Package do not automatically fall
|
||||
under the copyright of this Package, but belong to whoever generated
|
||||
them, and may be sold commercially, and may be aggregated with this
|
||||
Package. If such scripts or library files are aggregated with this
|
||||
Package via the so-called "undump" or "unexec" methods of producing a
|
||||
binary executable image, then distribution of such an image shall
|
||||
neither be construed as a distribution of this Package nor shall it
|
||||
fall under the restrictions of Paragraphs 3 and 4, provided that you do
|
||||
not represent such an executable image as a Standard Version of this
|
||||
Package.
|
||||
|
||||
7. C subroutines (or comparably compiled subroutines in other
|
||||
languages) supplied by you and linked into this Package in order to
|
||||
emulate subroutines and variables of the language defined by this
|
||||
Package shall not be considered part of this Package, but are the
|
||||
equivalent of input as in Paragraph 6, provided these subroutines do
|
||||
not change the language in any way that would cause it to fail the
|
||||
regression tests for the language.
|
||||
|
||||
8. Aggregation of this Package with a commercial distribution is always
|
||||
permitted provided that the use of this Package is embedded; that is,
|
||||
when no overt attempt is made to make this Package's interfaces visible
|
||||
to the end user of the commercial distribution. Such use shall not be
|
||||
construed as a distribution of this Package.
|
||||
|
||||
9. The name of the Copyright Holder may not be used to endorse or promote
|
||||
products derived from this software without specific prior written permission.
|
||||
|
||||
10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
The End
|
||||
*/
|
||||
/*****************************************************************************/
|
||||
/* GNU GENERAL PUBLIC LICENSE: */
|
||||
/*****************************************************************************/
|
||||
/* This program is free software; you can redistribute it and/or */
|
||||
/* modify it under the terms of the GNU General Public License */
|
||||
/* as published by the Free Software Foundation; either version 2 */
|
||||
/* of the License, or (at your option) any later version. */
|
||||
/* */
|
||||
/* This program is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
||||
/* GNU General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU General Public License */
|
||||
/* along with this program; if not, write to the */
|
||||
/* Free Software Foundation, Inc., */
|
||||
/* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
/* GNU LIBRARY GENERAL PUBLIC LICENSE: */
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* This library is free software; you can redistribute it and/or */
|
||||
/* modify it under the terms of the GNU Library General Public */
|
||||
/* License as published by the Free Software Foundation; either */
|
||||
/* version 2 of the License, or (at your option) any later version. */
|
||||
/* */
|
||||
/* This library is distributed in the hope that it will be useful, */
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
|
||||
/* Library General Public License for more details. */
|
||||
/* */
|
||||
/* You should have received a copy of the GNU Library General Public */
|
||||
/* License along with this library; if not, write to the */
|
||||
/* Free Software Foundation, Inc., */
|
||||
/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
||||
/* */
|
||||
/* or download a copy from ftp://ftp.gnu.org/pub/gnu/COPYING.LIB-2.0 */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
#endif
|
||||
@ -0,0 +1,638 @@
|
||||
/**
|
||||
* \file libyasm/bytecode.h
|
||||
* \brief YASM bytecode interface.
|
||||
*
|
||||
* \license
|
||||
* Copyright (C) 2001-2007 Peter Johnson
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* \endlicense
|
||||
*/
|
||||
#ifndef YASM_BYTECODE_H
|
||||
#define YASM_BYTECODE_H
|
||||
|
||||
#ifndef YASM_LIB_DECL
|
||||
#define YASM_LIB_DECL
|
||||
#endif
|
||||
|
||||
/** A data value (opaque type). */
|
||||
typedef struct yasm_dataval yasm_dataval;
|
||||
/** A list of data values. */
|
||||
typedef struct yasm_datavalhead yasm_datavalhead;
|
||||
|
||||
/** Linked list of data values. */
|
||||
/*@reldef@*/ STAILQ_HEAD(yasm_datavalhead, yasm_dataval);
|
||||
|
||||
/** Add a dependent span for a bytecode.
|
||||
* \param add_span_data add_span_data passed into bc_calc_len()
|
||||
* \param bc bytecode containing span
|
||||
* \param id non-zero identifier for span; may be any non-zero value
|
||||
* if <0, expand is called for any change;
|
||||
* if >0, expand is only called when exceeds threshold
|
||||
* \param value dependent value for bytecode expansion
|
||||
* \param neg_thres negative threshold for long/short decision
|
||||
* \param pos_thres positive threshold for long/short decision
|
||||
*/
|
||||
typedef void (*yasm_bc_add_span_func)
|
||||
(void *add_span_data, yasm_bytecode *bc, int id, const yasm_value *value,
|
||||
long neg_thres, long pos_thres);
|
||||
|
||||
/** Bytecode callback structure. Any implementation of a specific bytecode
|
||||
* must implement these functions and this callback structure. The bytecode
|
||||
* implementation-specific data is stored in #yasm_bytecode.contents.
|
||||
*/
|
||||
typedef struct yasm_bytecode_callback {
|
||||
/** Destroys the implementation-specific data.
|
||||
* Called from yasm_bc_destroy().
|
||||
* \param contents #yasm_bytecode.contents
|
||||
*/
|
||||
void (*destroy) (/*@only@*/ void *contents);
|
||||
|
||||
/** Prints the implementation-specific data (for debugging purposes).
|
||||
* Called from yasm_bc_print().
|
||||
* \param contents #yasm_bytecode.contents
|
||||
* \param f file
|
||||
* \param indent_level indentation level
|
||||
*/
|
||||
void (*print) (const void *contents, FILE *f, int indent_level);
|
||||
|
||||
/** Finalizes the bytecode after parsing. Called from yasm_bc_finalize().
|
||||
* A generic fill-in for this is yasm_bc_finalize_common().
|
||||
* \param bc bytecode
|
||||
* \param prev_bc bytecode directly preceding bc
|
||||
*/
|
||||
void (*finalize) (yasm_bytecode *bc, yasm_bytecode *prev_bc);
|
||||
|
||||
/** Return elements size of a data bytecode.
|
||||
* This function should return the size of each elements of a data
|
||||
* bytecode, for proper dereference of symbols attached to it.
|
||||
* \param bc bytecode
|
||||
* \return 0 if element size is unknown.
|
||||
*/
|
||||
int (*elem_size) (yasm_bytecode *bc);
|
||||
|
||||
/** Calculates the minimum size of a bytecode.
|
||||
* Called from yasm_bc_calc_len().
|
||||
* A generic fill-in for this is yasm_bc_calc_len_common(), but as this
|
||||
* function internal errors when called, be very careful when using it!
|
||||
* This function should simply add to bc->len and not set it directly
|
||||
* (it's initialized by yasm_bc_calc_len() prior to passing control to
|
||||
* this function).
|
||||
*
|
||||
* \param bc bytecode
|
||||
* \param add_span function to call to add a span
|
||||
* \param add_span_data extra data to be passed to add_span function
|
||||
* \return 0 if no error occurred, nonzero if there was an error
|
||||
* recognized (and output) during execution.
|
||||
* \note May store to bytecode updated expressions.
|
||||
*/
|
||||
int (*calc_len) (yasm_bytecode *bc, yasm_bc_add_span_func add_span,
|
||||
void *add_span_data);
|
||||
|
||||
/** Recalculates the bytecode's length based on an expanded span length.
|
||||
* Called from yasm_bc_expand().
|
||||
* A generic fill-in for this is yasm_bc_expand_common(), but as this
|
||||
* function internal errors when called, if used, ensure that calc_len()
|
||||
* never adds a span.
|
||||
* This function should simply add to bc->len to increase the length by
|
||||
* a delta amount.
|
||||
* \param bc bytecode
|
||||
* \param span span ID (as given to add_span in calc_len)
|
||||
* \param old_val previous span value
|
||||
* \param new_val new span value
|
||||
* \param neg_thres negative threshold for long/short decision
|
||||
* (returned)
|
||||
* \param pos_thres positive threshold for long/short decision
|
||||
* (returned)
|
||||
* \return 0 if bc no longer dependent on this span's length, negative if
|
||||
* there was an error recognized (and output) during execution,
|
||||
* and positive if bc size may increase for this span further
|
||||
* based on the new negative and positive thresholds returned.
|
||||
* \note May store to bytecode updated expressions.
|
||||
*/
|
||||
int (*expand) (yasm_bytecode *bc, int span, long old_val, long new_val,
|
||||
/*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres);
|
||||
|
||||
/** Convert a bytecode into its byte representation.
|
||||
* Called from yasm_bc_tobytes().
|
||||
* A generic fill-in for this is yasm_bc_tobytes_common(), but as this
|
||||
* function internal errors when called, be very careful when using it!
|
||||
* \param bc bytecode
|
||||
* \param bufp byte representation destination buffer;
|
||||
* should be incremented as it's written to,
|
||||
* so that on return its delta from the
|
||||
* passed-in buf matches the bytecode length
|
||||
* (it's okay not to do this if an error
|
||||
* indication is returned)
|
||||
* \param bufstart For calculating the correct offset parameter for
|
||||
* the \a output_value calls: *bufp - bufstart.
|
||||
* \param d data to pass to each call to
|
||||
* output_value/output_reloc
|
||||
* \param output_value function to call to convert values into their byte
|
||||
* representation
|
||||
* \param output_reloc function to call to output relocation entries
|
||||
* for a single sym
|
||||
* \return Nonzero on error, 0 on success.
|
||||
* \note May result in non-reversible changes to the bytecode, but it's
|
||||
* preferable if calling this function twice would result in the
|
||||
* same output.
|
||||
*/
|
||||
int (*tobytes) (yasm_bytecode *bc, unsigned char **bufp,
|
||||
unsigned char *bufstart, void *d,
|
||||
yasm_output_value_func output_value,
|
||||
/*@null@*/ yasm_output_reloc_func output_reloc);
|
||||
|
||||
/** Special bytecode classifications. Most bytecode types should use
|
||||
* #YASM_BC_SPECIAL_NONE. Others cause special handling to kick in
|
||||
* in various parts of yasm.
|
||||
*/
|
||||
enum yasm_bytecode_special_type {
|
||||
YASM_BC_SPECIAL_NONE = 0,
|
||||
|
||||
/** Bytecode reserves space instead of outputting data. */
|
||||
YASM_BC_SPECIAL_RESERVE,
|
||||
|
||||
/** Adjusts offset instead of calculating len. */
|
||||
YASM_BC_SPECIAL_OFFSET,
|
||||
|
||||
/** Instruction bytecode. */
|
||||
YASM_BC_SPECIAL_INSN
|
||||
} special;
|
||||
} yasm_bytecode_callback;
|
||||
|
||||
/** A bytecode. */
|
||||
struct yasm_bytecode {
|
||||
/** Bytecodes are stored as a singly linked list, with tail insertion.
|
||||
* \see section.h (#yasm_section).
|
||||
*/
|
||||
/*@reldef@*/ STAILQ_ENTRY(yasm_bytecode) link;
|
||||
|
||||
/** The bytecode callback structure for this bytecode. May be NULL
|
||||
* during partial initialization.
|
||||
*/
|
||||
/*@null@*/ const yasm_bytecode_callback *callback;
|
||||
|
||||
/** Pointer to section containing bytecode; NULL if not part of a
|
||||
* section.
|
||||
*/
|
||||
/*@dependent@*/ /*@null@*/ yasm_section *section;
|
||||
|
||||
/** Number of times bytecode is repeated.
|
||||
* NULL=1 (to save space in the common case).
|
||||
*/
|
||||
/*@only@*/ /*@null@*/ yasm_expr *multiple;
|
||||
|
||||
/** Total length of entire bytecode (not including multiple copies). */
|
||||
unsigned long len;
|
||||
|
||||
/** Number of copies, integer version. */
|
||||
long mult_int;
|
||||
|
||||
/** Line number where bytecode was defined. */
|
||||
unsigned long line;
|
||||
|
||||
/** Offset of bytecode from beginning of its section.
|
||||
* 0-based, ~0UL (e.g. all 1 bits) if unknown.
|
||||
*/
|
||||
unsigned long offset;
|
||||
|
||||
/** Unique integer index of bytecode. Used during optimization. */
|
||||
unsigned long bc_index;
|
||||
|
||||
/** NULL-terminated array of labels that point to this bytecode (as the
|
||||
* bytecode previous to the label). NULL if no labels point here.
|
||||
*/
|
||||
/*@null@*/ yasm_symrec **symrecs;
|
||||
|
||||
/** Implementation-specific data (type identified by callback). */
|
||||
void *contents;
|
||||
};
|
||||
|
||||
/** Create a bytecode of any specified type.
|
||||
* \param callback bytecode callback functions, if NULL, creates empty
|
||||
* bytecode (may not be resolved or output)
|
||||
* \param contents type-specific data
|
||||
* \param line virtual line (from yasm_linemap)
|
||||
* \return Newly allocated bytecode of the specified type.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_bytecode *yasm_bc_create_common
|
||||
(/*@null@*/ const yasm_bytecode_callback *callback,
|
||||
/*@only@*/ /*@null@*/ void *contents, unsigned long line);
|
||||
|
||||
/** Transform a bytecode of any type into a different type.
|
||||
* \param bc bytecode to transform
|
||||
* \param callback new bytecode callback function
|
||||
* \param contents new type-specific data
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_bc_transform(yasm_bytecode *bc,
|
||||
const yasm_bytecode_callback *callback,
|
||||
void *contents);
|
||||
|
||||
/** Common bytecode callback finalize function, for where no finalization
|
||||
* is ever required for this type of bytecode.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_bc_finalize_common(yasm_bytecode *bc, yasm_bytecode *prev_bc);
|
||||
|
||||
/** Common bytecode callback calc_len function, for where the bytecode has
|
||||
* no calculatable length. Causes an internal error if called.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_bc_calc_len_common(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
|
||||
void *add_span_data);
|
||||
|
||||
/** Common bytecode callback expand function, for where the bytecode is
|
||||
* always short (calc_len never calls add_span). Causes an internal
|
||||
* error if called.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_bc_expand_common
|
||||
(yasm_bytecode *bc, int span, long old_val, long new_val,
|
||||
/*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres);
|
||||
|
||||
/** Common bytecode callback tobytes function, for where the bytecode
|
||||
* cannot be converted to bytes. Causes an internal error if called.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_bc_tobytes_common
|
||||
(yasm_bytecode *bc, unsigned char **bufp, unsigned char *bufstart, void *d,
|
||||
yasm_output_value_func output_value,
|
||||
/*@null@*/ yasm_output_reloc_func output_reloc);
|
||||
|
||||
/** Get the next bytecode in a linked list of bytecodes.
|
||||
* \param bc bytecode
|
||||
* \return Next bytecode.
|
||||
*/
|
||||
#define yasm_bc__next(bc) STAILQ_NEXT(bc, link)
|
||||
|
||||
/** Set multiple field of a bytecode.
|
||||
* A bytecode can be repeated a number of times when output. This function
|
||||
* sets that multiple.
|
||||
* \param bc bytecode
|
||||
* \param e multiple (kept, do not free)
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_bc_set_multiple(yasm_bytecode *bc, /*@keep@*/ yasm_expr *e);
|
||||
|
||||
/** Create a bytecode containing data value(s).
|
||||
* \param datahead list of data values (kept, do not free)
|
||||
* \param size storage size (in bytes) for each data value
|
||||
* \param append_zero append a single zero byte after each data value
|
||||
* (if non-zero)
|
||||
* \param arch architecture (optional); if provided, data items
|
||||
* are directly simplified to bytes if possible
|
||||
* \param line virtual line (from yasm_linemap)
|
||||
* \return Newly allocated bytecode.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_bytecode *yasm_bc_create_data
|
||||
(yasm_datavalhead *datahead, unsigned int size, int append_zero,
|
||||
/*@null@*/ yasm_arch *arch, unsigned long line);
|
||||
|
||||
/** Create a bytecode containing LEB128-encoded data value(s).
|
||||
* \param datahead list of data values (kept, do not free)
|
||||
* \param sign signedness (1=signed, 0=unsigned) of each data value
|
||||
* \param line virtual line (from yasm_linemap)
|
||||
* \return Newly allocated bytecode.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_bytecode *yasm_bc_create_leb128
|
||||
(yasm_datavalhead *datahead, int sign, unsigned long line);
|
||||
|
||||
/** Create a bytecode reserving space.
|
||||
* \param numitems number of reserve "items" (kept, do not free)
|
||||
* \param itemsize reserved size (in bytes) for each item
|
||||
* \param line virtual line (from yasm_linemap)
|
||||
* \return Newly allocated bytecode.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_bytecode *yasm_bc_create_reserve
|
||||
(/*@only@*/ yasm_expr *numitems, unsigned int itemsize,
|
||||
unsigned long line);
|
||||
|
||||
/** Get the number of items and itemsize for a reserve bytecode. If bc
|
||||
* is not a reserve bytecode, returns NULL.
|
||||
* \param bc bytecode
|
||||
* \param itemsize reserved size (in bytes) for each item (returned)
|
||||
* \return NULL if bc is not a reserve bytecode, otherwise an expression
|
||||
* for the number of items to reserve.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@null@*/ const yasm_expr *yasm_bc_reserve_numitems
|
||||
(yasm_bytecode *bc, /*@out@*/ unsigned int *itemsize);
|
||||
|
||||
/** Create a bytecode that includes a binary file verbatim.
|
||||
* \param filename path to binary file (kept, do not free)
|
||||
* \param start starting location in file (in bytes) to read data from
|
||||
* (kept, do not free); may be NULL to indicate 0
|
||||
* \param maxlen maximum number of bytes to read from the file (kept, do
|
||||
* do not free); may be NULL to indicate no maximum
|
||||
* \param linemap line mapping repository
|
||||
* \param line virtual line (from yasm_linemap) for the bytecode
|
||||
* \return Newly allocated bytecode.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_bytecode *yasm_bc_create_incbin
|
||||
(/*@only@*/ char *filename, /*@only@*/ /*@null@*/ yasm_expr *start,
|
||||
/*@only@*/ /*@null@*/ yasm_expr *maxlen, yasm_linemap *linemap,
|
||||
unsigned long line);
|
||||
|
||||
/** Create a bytecode that aligns the following bytecode to a boundary.
|
||||
* \param boundary byte alignment (must be a power of two)
|
||||
* \param fill fill data (if NULL, code_fill or 0 is used)
|
||||
* \param maxskip maximum number of bytes to skip
|
||||
* \param code_fill code fill data (if NULL, 0 is used)
|
||||
* \param line virtual line (from yasm_linemap)
|
||||
* \return Newly allocated bytecode.
|
||||
* \note The precedence on generated fill is as follows:
|
||||
* - from fill parameter (if not NULL)
|
||||
* - from code_fill parameter (if not NULL)
|
||||
* - 0
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_bytecode *yasm_bc_create_align
|
||||
(/*@keep@*/ yasm_expr *boundary, /*@keep@*/ /*@null@*/ yasm_expr *fill,
|
||||
/*@keep@*/ /*@null@*/ yasm_expr *maxskip,
|
||||
/*@null@*/ const unsigned char **code_fill, unsigned long line);
|
||||
|
||||
/** Create a bytecode that puts the following bytecode at a fixed section
|
||||
* offset.
|
||||
* \param start section offset of following bytecode
|
||||
* \param fill fill value
|
||||
* \param line virtual line (from yasm_linemap)
|
||||
* \return Newly allocated bytecode.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_bytecode *yasm_bc_create_org
|
||||
(unsigned long start, unsigned long fill, unsigned long line);
|
||||
|
||||
/** Get the section that contains a particular bytecode.
|
||||
* \param bc bytecode
|
||||
* \return Section containing bc (can be NULL if bytecode is not part of a
|
||||
* section).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@dependent@*/ /*@null@*/ yasm_section *yasm_bc_get_section
|
||||
(yasm_bytecode *bc);
|
||||
|
||||
/** Add to the list of symrecs that reference a bytecode. For symrec use
|
||||
* only.
|
||||
* \param bc bytecode
|
||||
* \param sym symbol
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_bc__add_symrec(yasm_bytecode *bc, /*@dependent@*/ yasm_symrec *sym);
|
||||
|
||||
/** Delete (free allocated memory for) a bytecode.
|
||||
* \param bc bytecode (only pointer to it); may be NULL
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_bc_destroy(/*@only@*/ /*@null@*/ yasm_bytecode *bc);
|
||||
|
||||
/** Print a bytecode. For debugging purposes.
|
||||
* \param f file
|
||||
* \param indent_level indentation level
|
||||
* \param bc bytecode
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_bc_print(const yasm_bytecode *bc, FILE *f, int indent_level);
|
||||
|
||||
/** Finalize a bytecode after parsing.
|
||||
* \param bc bytecode
|
||||
* \param prev_bc bytecode directly preceding bc in a list of bytecodes
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc);
|
||||
|
||||
/** Determine the distance between the starting offsets of two bytecodes.
|
||||
* \param precbc1 preceding bytecode to the first bytecode
|
||||
* \param precbc2 preceding bytecode to the second bytecode
|
||||
* \return Distance in bytes between the two bytecodes (bc2-bc1), or NULL if
|
||||
* the distance was indeterminate.
|
||||
* \warning Only valid /after/ optimization.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@null@*/ /*@only@*/ yasm_intnum *yasm_calc_bc_dist
|
||||
(yasm_bytecode *precbc1, yasm_bytecode *precbc2);
|
||||
|
||||
/** Get the offset of the next bytecode (the next bytecode doesn't have to
|
||||
* actually exist).
|
||||
* \param precbc preceding bytecode
|
||||
* \return Offset of the next bytecode in bytes.
|
||||
* \warning Only valid /after/ optimization.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
unsigned long yasm_bc_next_offset(yasm_bytecode *precbc);
|
||||
|
||||
/** Return elemens size of a data bytecode.
|
||||
* Returns the size of each elements of a data bytecode, for proper dereference
|
||||
* of symbols attached to it.
|
||||
* \param bc bytecode
|
||||
* \return 0 if element size is unknown
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_bc_elem_size(yasm_bytecode *bc);
|
||||
|
||||
/** Resolve EQUs in a bytecode and calculate its minimum size.
|
||||
* Generates dependent bytecode spans for cases where, if the length spanned
|
||||
* increases, it could cause the bytecode size to increase.
|
||||
* Any bytecode multiple is NOT included in the length or spans generation;
|
||||
* this must be handled at a higher level.
|
||||
* \param bc bytecode
|
||||
* \param add_span function to call to add a span
|
||||
* \param add_span_data extra data to be passed to add_span function
|
||||
* \return 0 if no error occurred, nonzero if there was an error recognized
|
||||
* (and output) during execution.
|
||||
* \note May store to bytecode updated expressions and the short length.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
|
||||
void *add_span_data);
|
||||
|
||||
/** Recalculate a bytecode's length based on an expanded span length.
|
||||
* \param bc bytecode
|
||||
* \param span span ID (as given to yasm_bc_add_span_func in
|
||||
* yasm_bc_calc_len)
|
||||
* \param old_val previous span value
|
||||
* \param new_val new span value
|
||||
* \param neg_thres negative threshold for long/short decision (returned)
|
||||
* \param pos_thres positive threshold for long/short decision (returned)
|
||||
* \return 0 if bc no longer dependent on this span's length, negative if
|
||||
* there was an error recognized (and output) during execution, and
|
||||
* positive if bc size may increase for this span further based on the
|
||||
* new negative and positive thresholds returned.
|
||||
* \note May store to bytecode updated expressions and the updated length.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
|
||||
/*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres);
|
||||
|
||||
/** Convert a bytecode into its byte representation.
|
||||
* \param bc bytecode
|
||||
* \param buf byte representation destination buffer
|
||||
* \param bufsize size of buf (in bytes) prior to call; size of the
|
||||
* generated data after call
|
||||
* \param gap if nonzero, indicates the data does not really need to
|
||||
* exist in the object file; if nonzero, contents of buf
|
||||
* are undefined [output]
|
||||
* \param d data to pass to each call to output_value/output_reloc
|
||||
* \param output_value function to call to convert values into their byte
|
||||
* representation
|
||||
* \param output_reloc function to call to output relocation entries
|
||||
* for a single sym
|
||||
* \return Newly allocated buffer that should be used instead of buf for
|
||||
* reading the byte representation, or NULL if buf was big enough to
|
||||
* hold the entire byte representation.
|
||||
* \note Calling twice on the same bytecode may \em not produce the same
|
||||
* results on the second call, as calling this function may result in
|
||||
* non-reversible changes to the bytecode.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@null@*/ /*@only@*/ unsigned char *yasm_bc_tobytes
|
||||
(yasm_bytecode *bc, unsigned char *buf, unsigned long *bufsize,
|
||||
/*@out@*/ int *gap, void *d, yasm_output_value_func output_value,
|
||||
/*@null@*/ yasm_output_reloc_func output_reloc)
|
||||
/*@sets *buf@*/;
|
||||
|
||||
/** Get the bytecode multiple value as an integer.
|
||||
* \param bc bytecode
|
||||
* \param multiple multiple value (output)
|
||||
* \param calc_bc_dist nonzero if distances between bytecodes should be
|
||||
* calculated, 0 if error should be returned in this case
|
||||
* \return 1 on error (set with yasm_error_set), 0 on success.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_bc_get_multiple(yasm_bytecode *bc, /*@out@*/ long *multiple,
|
||||
int calc_bc_dist);
|
||||
|
||||
/** Get the bytecode multiple value as an expression.
|
||||
* \param bc bytecode
|
||||
* \return Bytecode multiple, NULL if =1.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
const yasm_expr *yasm_bc_get_multiple_expr(const yasm_bytecode *bc);
|
||||
|
||||
/** Get a #yasm_insn structure from an instruction bytecode (if possible).
|
||||
* \param bc bytecode
|
||||
* \return Instruction details if bytecode is an instruction bytecode,
|
||||
* otherwise NULL.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@dependent@*/ /*@null@*/ yasm_insn *yasm_bc_get_insn(yasm_bytecode *bc);
|
||||
|
||||
/** Create a new data value from an expression.
|
||||
* \param expn expression
|
||||
* \return Newly allocated data value.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
yasm_dataval *yasm_dv_create_expr(/*@keep@*/ yasm_expr *expn);
|
||||
|
||||
/** Create a new data value from a string.
|
||||
* \param contents string (may contain NULs)
|
||||
* \param len length of string
|
||||
* \return Newly allocated data value.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
yasm_dataval *yasm_dv_create_string(/*@keep@*/ char *contents, size_t len);
|
||||
|
||||
/** Create a new data value from raw bytes data.
|
||||
* \param contents raw data (may contain NULs)
|
||||
* \param len length
|
||||
* \return Newly allocated data value.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
yasm_dataval *yasm_dv_create_raw(/*@keep@*/ unsigned char *contents,
|
||||
unsigned long len);
|
||||
|
||||
/** Create a new uninitialized data value.
|
||||
* \return Newly allocated data value.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
yasm_dataval *yasm_dv_create_reserve(void);
|
||||
|
||||
#ifndef YASM_DOXYGEN
|
||||
#define yasm_dv_create_string(s, l) yasm_dv_create_raw((unsigned char *)(s), \
|
||||
(unsigned long)(l))
|
||||
#endif
|
||||
|
||||
/** Get the underlying value of a data value.
|
||||
* \param dv data value
|
||||
* \return Value, or null if non-value (e.g. string or raw).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
yasm_value *yasm_dv_get_value(yasm_dataval *dv);
|
||||
|
||||
/** Set multiple field of a data value.
|
||||
* A data value can be repeated a number of times when output. This function
|
||||
* sets that multiple.
|
||||
* \param dv data value
|
||||
* \param e multiple (kept, do not free)
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_dv_set_multiple(yasm_dataval *dv, /*@keep@*/ yasm_expr *e);
|
||||
|
||||
/** Get the data value multiple value as an unsigned long integer.
|
||||
* \param dv data value
|
||||
* \param multiple multiple value (output)
|
||||
* \return 1 on error (set with yasm_error_set), 0 on success.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_dv_get_multiple(yasm_dataval *dv, /*@out@*/ unsigned long *multiple);
|
||||
|
||||
/** Initialize a list of data values.
|
||||
* \param headp list of data values
|
||||
*/
|
||||
void yasm_dvs_initialize(yasm_datavalhead *headp);
|
||||
#ifndef YASM_DOXYGEN
|
||||
#define yasm_dvs_initialize(headp) STAILQ_INIT(headp)
|
||||
#endif
|
||||
|
||||
/** Delete (free allocated memory for) a list of data values.
|
||||
* \param headp list of data values
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_dvs_delete(yasm_datavalhead *headp);
|
||||
|
||||
/** Add data value to the end of a list of data values.
|
||||
* \note Does not make a copy of the data value; so don't pass this function
|
||||
* static or local variables, and discard the dv pointer after calling
|
||||
* this function.
|
||||
* \param headp data value list
|
||||
* \param dv data value (may be NULL)
|
||||
* \return If data value was actually appended (it wasn't NULL), the data
|
||||
* value; otherwise NULL.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@null@*/ yasm_dataval *yasm_dvs_append
|
||||
(yasm_datavalhead *headp, /*@returned@*/ /*@null@*/ yasm_dataval *dv);
|
||||
|
||||
/** Print a data value list. For debugging purposes.
|
||||
* \param f file
|
||||
* \param indent_level indentation level
|
||||
* \param headp data value list
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_dvs_print(const yasm_datavalhead *headp, FILE *f, int indent_level);
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,456 @@
|
||||
/*
|
||||
* <sys/queue.h> implementation for systems that don't have it.
|
||||
*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
* $FreeBSD: src/sys/sys/queue.h,v 1.32.2.4 2001/03/31 03:33:39 hsu Exp $
|
||||
*/
|
||||
|
||||
#ifndef SYS_QUEUE_H
|
||||
#define SYS_QUEUE_H
|
||||
|
||||
/*
|
||||
* This file defines four types of data structures: singly-linked lists,
|
||||
* singly-linked tail queues, lists and tail queues.
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A singly-linked tail queue is headed by a pair of pointers, one to the
|
||||
* head of the list and the other to the tail of the list. The elements are
|
||||
* singly linked for minimum space and pointer manipulation overhead at the
|
||||
* expense of O(n) removal for arbitrary elements. New elements can be added
|
||||
* to the list after an existing element, at the head of the list, or at the
|
||||
* end of the list. Elements being removed from the head of the tail queue
|
||||
* should use the explicit macro for this purpose for optimum efficiency.
|
||||
* A singly-linked tail queue may only be traversed in the forward direction.
|
||||
* Singly-linked tail queues are ideal for applications with large datasets
|
||||
* and few or no removals or for implementing a FIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*
|
||||
*
|
||||
* SLIST LIST STAILQ TAILQ
|
||||
* _HEAD + + + +
|
||||
* _HEAD_INITIALIZER + + + +
|
||||
* _ENTRY + + + +
|
||||
* _INIT + + + +
|
||||
* _EMPTY + + + +
|
||||
* _FIRST + + + +
|
||||
* _NEXT + + + +
|
||||
* _PREV - - - +
|
||||
* _LAST - - + +
|
||||
* _FOREACH + + + +
|
||||
* _FOREACH_SAFE + + + +
|
||||
* _FOREACH_REVERSE - - - +
|
||||
* _FOREACH_REVERSE_SAFE - - - +
|
||||
* _INSERT_HEAD + + + +
|
||||
* _INSERT_BEFORE - + - +
|
||||
* _INSERT_AFTER + + + +
|
||||
* _INSERT_TAIL - - + +
|
||||
* _CONCAT - - + +
|
||||
* _REMOVE_HEAD + - + -
|
||||
* _REMOVE + + + +
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Singly-linked List declarations.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
||||
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
|
||||
for ((varp) = &SLIST_FIRST((head)); \
|
||||
((var) = *(varp)) != NULL; \
|
||||
(varp) = &SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_INIT(head) do { \
|
||||
SLIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
||||
SLIST_NEXT((slistelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
||||
SLIST_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if (SLIST_FIRST((head)) == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = SLIST_FIRST((head)); \
|
||||
while (SLIST_NEXT(curelm, field) != (elm)) \
|
||||
curelm = SLIST_NEXT(curelm, field); \
|
||||
SLIST_NEXT(curelm, field) = \
|
||||
SLIST_NEXT(SLIST_NEXT(curelm, field), field); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue declarations.
|
||||
*/
|
||||
#define STAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *stqh_first;/* first element */ \
|
||||
struct type **stqh_last;/* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).stqh_first }
|
||||
|
||||
#define STAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue functions.
|
||||
*/
|
||||
#define STAILQ_CONCAT(head1, head2) do { \
|
||||
if (!STAILQ_EMPTY((head2))) { \
|
||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
|
||||
|
||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
||||
|
||||
#define STAILQ_FOREACH(var, head, field) \
|
||||
for((var) = STAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
|
||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = STAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_INIT(head) do { \
|
||||
STAILQ_FIRST((head)) = NULL; \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_NEXT((tqelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
STAILQ_NEXT((elm), field) = NULL; \
|
||||
*(head)->stqh_last = (elm); \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_LAST(head, type, field) \
|
||||
(STAILQ_EMPTY((head)) ? \
|
||||
NULL : \
|
||||
((struct type *) \
|
||||
((char *)((head)->stqh_last) - offsetof(struct type, field))))
|
||||
|
||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
||||
|
||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
||||
STAILQ_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = STAILQ_FIRST((head)); \
|
||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
||||
curelm = STAILQ_NEXT(curelm, field); \
|
||||
if ((STAILQ_NEXT(curelm, field) = \
|
||||
STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
|
||||
(head)->stqh_last = &STAILQ_NEXT((curelm), field);\
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = \
|
||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* List declarations.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
|
||||
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
|
||||
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = LIST_NEXT((var), field))
|
||||
|
||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
|
||||
LIST_NEXT((listelm), field)->field.le_prev = \
|
||||
&LIST_NEXT((elm), field); \
|
||||
LIST_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
LIST_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
|
||||
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
|
||||
LIST_FIRST((head)) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
if (LIST_NEXT((elm), field) != NULL) \
|
||||
LIST_NEXT((elm), field)->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Tail queue declarations.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
||||
if (!TAILQ_EMPTY(head2)) { \
|
||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
TAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
||||
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = TAILQ_NEXT((var), field))
|
||||
|
||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var); \
|
||||
(var) = TAILQ_PREV((var), headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_INIT(head) do { \
|
||||
TAILQ_FIRST((head)) = NULL; \
|
||||
(head)->tqh_last = &TAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else { \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
} \
|
||||
TAILQ_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
TAILQ_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
|
||||
TAILQ_FIRST((head))->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
TAILQ_FIRST((head)) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
TAILQ_NEXT((elm), field) = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field)) != NULL) \
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else { \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
} \
|
||||
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#endif /* !SYS_QUEUE_H */
|
||||
@ -0,0 +1,393 @@
|
||||
/**
|
||||
* \file libyasm/coretype.h
|
||||
* \brief YASM core types and utility functions.
|
||||
*
|
||||
* \license
|
||||
* Copyright (C) 2001-2007 Peter Johnson
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* \endlicense
|
||||
*/
|
||||
#ifndef YASM_CORETYPE_H
|
||||
#define YASM_CORETYPE_H
|
||||
|
||||
#ifndef YASM_LIB_DECL
|
||||
#define YASM_LIB_DECL
|
||||
#endif
|
||||
|
||||
/** Architecture instance (mostly opaque type). \see arch.h for details. */
|
||||
typedef struct yasm_arch yasm_arch;
|
||||
/** Preprocessor interface. \see preproc.h for details. */
|
||||
typedef struct yasm_preproc yasm_preproc;
|
||||
/** Parser instance (mostly opaque type). \see parser.h for details. */
|
||||
typedef struct yasm_parser yasm_parser;
|
||||
/** Object format interface. \see objfmt.h for details. */
|
||||
typedef struct yasm_objfmt yasm_objfmt;
|
||||
/** Debug format interface. \see dbgfmt.h for details. */
|
||||
typedef struct yasm_dbgfmt yasm_dbgfmt;
|
||||
/** List format interface. \see listfmt.h for details. */
|
||||
typedef struct yasm_listfmt yasm_listfmt;
|
||||
|
||||
/** Object format module interface. \see objfmt.h for details. */
|
||||
typedef struct yasm_objfmt_module yasm_objfmt_module;
|
||||
/** Debug format module interface. \see dbgfmt.h for details. */
|
||||
typedef struct yasm_dbgfmt_module yasm_dbgfmt_module;
|
||||
|
||||
/** Standard macro structure for modules that allows association of a set of
|
||||
* standard macros with a parser/preprocessor combination.
|
||||
* A NULL-terminated array of these structures is used in a number of module
|
||||
* interfaces.
|
||||
*/
|
||||
typedef struct yasm_stdmac {
|
||||
const char *parser; /**< Parser keyword */
|
||||
const char *preproc; /**< Preprocessor keyword */
|
||||
|
||||
/** NULL-terminated array of standard macros. May be NULL if no standard
|
||||
* macros should be added for this preprocessor.
|
||||
*/
|
||||
const char **macros;
|
||||
} yasm_stdmac;
|
||||
|
||||
/** YASM associated data callback structure. Many data structures can have
|
||||
* arbitrary data associated with them.
|
||||
*/
|
||||
typedef struct yasm_assoc_data_callback {
|
||||
/** Free memory allocated for associated data.
|
||||
* \param data associated data
|
||||
*/
|
||||
void (*destroy) (/*@only@*/ void *data);
|
||||
|
||||
/** Print a description of allocated data. For debugging purposes.
|
||||
* \param data associated data
|
||||
* \param f output file
|
||||
* \param indent_level indentation level
|
||||
*/
|
||||
void (*print) (void *data, FILE *f, int indent_level);
|
||||
} yasm_assoc_data_callback;
|
||||
|
||||
/** Set of collected error/warnings (opaque type).
|
||||
* \see errwarn.h for details.
|
||||
*/
|
||||
typedef struct yasm_errwarns yasm_errwarns;
|
||||
|
||||
/** Bytecode. \see bytecode.h for details and related functions. */
|
||||
typedef struct yasm_bytecode yasm_bytecode;
|
||||
|
||||
/** Object. \see section.h for details and related functions. */
|
||||
typedef struct yasm_object yasm_object;
|
||||
|
||||
/** Section (opaque type). \see section.h for related functions. */
|
||||
typedef struct yasm_section yasm_section;
|
||||
|
||||
/** Symbol table (opaque type). \see symrec.h for related functions. */
|
||||
typedef struct yasm_symtab yasm_symtab;
|
||||
|
||||
/** Symbol record (opaque type). \see symrec.h for related functions. */
|
||||
typedef struct yasm_symrec yasm_symrec;
|
||||
|
||||
/** Expression. \see expr.h for details and related functions. */
|
||||
typedef struct yasm_expr yasm_expr;
|
||||
/** Integer value (opaque type). \see intnum.h for related functions. */
|
||||
typedef struct yasm_intnum yasm_intnum;
|
||||
/** Floating point value (opaque type).
|
||||
* \see floatnum.h for related functions.
|
||||
*/
|
||||
typedef struct yasm_floatnum yasm_floatnum;
|
||||
|
||||
/** A value. May be absolute or relative. Outside the parser, yasm_expr
|
||||
* should only be used for absolute exprs. Anything that could contain
|
||||
* a relocatable value should use this structure instead.
|
||||
* \see value.h for related functions.
|
||||
*/
|
||||
typedef struct yasm_value {
|
||||
/** The absolute portion of the value. May contain *differences* between
|
||||
* symrecs but not standalone symrecs. May be NULL if there is no
|
||||
* absolute portion (e.g. the absolute portion is 0).
|
||||
*/
|
||||
/*@null@*/ /*@only@*/ yasm_expr *abs;
|
||||
|
||||
/** The relative portion of the value. This is the portion that may
|
||||
* need to generate a relocation. May be NULL if no relative portion.
|
||||
*/
|
||||
/*@null@*/ /*@dependent@*/ yasm_symrec *rel;
|
||||
|
||||
/** What the relative portion is in reference to. NULL if the default. */
|
||||
/*@null@*/ /*@dependent@*/ yasm_symrec *wrt;
|
||||
|
||||
/** If the segment of the relative portion should be used, not the
|
||||
* relative portion itself. Boolean.
|
||||
*/
|
||||
unsigned int seg_of : 1;
|
||||
|
||||
/** If the relative portion of the value should be shifted right
|
||||
* (supported only by a few object formats). If just the absolute portion
|
||||
* should be shifted, that must be in the abs expr, not here!
|
||||
*/
|
||||
unsigned int rshift : 7;
|
||||
|
||||
/** Indicates the relative portion of the value should be relocated
|
||||
* relative to the current assembly position rather than relative to the
|
||||
* section start. "Current assembly position" here refers to the starting
|
||||
* address of the bytecode containing this value. Boolean.
|
||||
*/
|
||||
unsigned int curpos_rel : 1;
|
||||
|
||||
/** Indicates that curpos_rel was set due to IP-relative relocation;
|
||||
* in some objfmt/arch combinations (e.g. win64/x86-amd64) this info
|
||||
* is needed to generate special relocations.
|
||||
*/
|
||||
unsigned int ip_rel : 1;
|
||||
|
||||
/** Indicates the value is a jump target address (rather than a simple
|
||||
* data address). In some objfmt/arch combinations (e.g. macho/amd64)
|
||||
* this info is needed to generate special relocations.
|
||||
*/
|
||||
unsigned int jump_target : 1;
|
||||
|
||||
/** Indicates the relative portion of the value should be relocated
|
||||
* relative to its own section start rather than relative to the
|
||||
* section start of the bytecode containing this value. E.g. the value
|
||||
* resulting from the relative portion should be the offset from its
|
||||
* section start. Boolean.
|
||||
*/
|
||||
unsigned int section_rel : 1;
|
||||
|
||||
/** Indicates overflow warnings have been disabled for this value. */
|
||||
unsigned int no_warn : 1;
|
||||
|
||||
/** Sign of the value. Nonzero if the final value should be treated as
|
||||
* signed, 0 if it should be treated as signed.
|
||||
*/
|
||||
unsigned int sign : 1;
|
||||
|
||||
/** Size of the value, in bits. */
|
||||
unsigned int size : 8;
|
||||
} yasm_value;
|
||||
|
||||
/** Maximum value of #yasm_value.rshift */
|
||||
#define YASM_VALUE_RSHIFT_MAX 127
|
||||
|
||||
/** Line number mapping repository (opaque type). \see linemap.h for related
|
||||
* functions.
|
||||
*/
|
||||
typedef struct yasm_linemap yasm_linemap;
|
||||
|
||||
/** Value/parameter pair (opaque type).
|
||||
* \see valparam.h for related functions.
|
||||
*/
|
||||
typedef struct yasm_valparam yasm_valparam;
|
||||
/** List of value/parameters (opaque type).
|
||||
* \see valparam.h for related functions.
|
||||
*/
|
||||
typedef struct yasm_valparamhead yasm_valparamhead;
|
||||
/** Directive list entry.
|
||||
* \see valparam.h for details and related functions.
|
||||
*/
|
||||
typedef struct yasm_directive yasm_directive;
|
||||
|
||||
/** An effective address.
|
||||
* \see insn.h for related functions.
|
||||
*/
|
||||
typedef struct yasm_effaddr yasm_effaddr;
|
||||
|
||||
/** An instruction.
|
||||
* \see insn.h for related functions.
|
||||
*/
|
||||
typedef struct yasm_insn yasm_insn;
|
||||
|
||||
/** Expression operators usable in #yasm_expr expressions. */
|
||||
typedef enum yasm_expr_op {
|
||||
YASM_EXPR_IDENT, /**< No operation, just a value. */
|
||||
YASM_EXPR_ADD, /**< Arithmetic addition (+). */
|
||||
YASM_EXPR_SUB, /**< Arithmetic subtraction (-). */
|
||||
YASM_EXPR_MUL, /**< Arithmetic multiplication (*). */
|
||||
YASM_EXPR_DIV, /**< Arithmetic unsigned division. */
|
||||
YASM_EXPR_SIGNDIV, /**< Arithmetic signed division. */
|
||||
YASM_EXPR_MOD, /**< Arithmetic unsigned modulus. */
|
||||
YASM_EXPR_SIGNMOD, /**< Arithmetic signed modulus. */
|
||||
YASM_EXPR_NEG, /**< Arithmetic negation (-). */
|
||||
YASM_EXPR_NOT, /**< Bitwise negation. */
|
||||
YASM_EXPR_OR, /**< Bitwise OR. */
|
||||
YASM_EXPR_AND, /**< Bitwise AND. */
|
||||
YASM_EXPR_XOR, /**< Bitwise XOR. */
|
||||
YASM_EXPR_XNOR, /**< Bitwise XNOR. */
|
||||
YASM_EXPR_NOR, /**< Bitwise NOR. */
|
||||
YASM_EXPR_SHL, /**< Shift left (logical). */
|
||||
YASM_EXPR_SHR, /**< Shift right (logical). */
|
||||
YASM_EXPR_LOR, /**< Logical OR. */
|
||||
YASM_EXPR_LAND, /**< Logical AND. */
|
||||
YASM_EXPR_LNOT, /**< Logical negation. */
|
||||
YASM_EXPR_LXOR, /**< Logical XOR. */
|
||||
YASM_EXPR_LXNOR, /**< Logical XNOR. */
|
||||
YASM_EXPR_LNOR, /**< Logical NOR. */
|
||||
YASM_EXPR_LT, /**< Less than comparison. */
|
||||
YASM_EXPR_GT, /**< Greater than comparison. */
|
||||
YASM_EXPR_EQ, /**< Equality comparison. */
|
||||
YASM_EXPR_LE, /**< Less than or equal to comparison. */
|
||||
YASM_EXPR_GE, /**< Greater than or equal to comparison. */
|
||||
YASM_EXPR_NE, /**< Not equal comparison. */
|
||||
YASM_EXPR_NONNUM, /**< Start of non-numeric operations (not an op). */
|
||||
YASM_EXPR_SEG, /**< SEG operator (gets segment portion of address). */
|
||||
YASM_EXPR_WRT, /**< WRT operator (gets offset of address relative to
|
||||
* some other segment). */
|
||||
YASM_EXPR_SEGOFF /**< The ':' in segment:offset. */
|
||||
} yasm_expr_op;
|
||||
|
||||
/** Convert yasm_value to its byte representation. Usually implemented by
|
||||
* object formats to keep track of relocations and verify legal expressions.
|
||||
* Must put the value into the least significant bits of the destination,
|
||||
* unless shifted into more significant bits by the shift parameter. The
|
||||
* destination bits must be cleared before being set.
|
||||
* \param value value
|
||||
* \param buf buffer for byte representation
|
||||
* \param destsize destination size (in bytes)
|
||||
* \param offset offset (in bytes) of the expr contents from the start
|
||||
* of the bytecode (needed for relative)
|
||||
* \param bc current bytecode (usually passed into higher-level
|
||||
* calling function)
|
||||
* \param warn enables standard warnings: zero for none;
|
||||
* nonzero for overflow/underflow floating point warnings
|
||||
* \param d objfmt-specific data (passed into higher-level calling
|
||||
* function)
|
||||
* \return Nonzero if an error occurred, 0 otherwise.
|
||||
*/
|
||||
typedef int (*yasm_output_value_func)
|
||||
(yasm_value *value, /*@out@*/ unsigned char *buf, unsigned int destsize,
|
||||
unsigned long offset, yasm_bytecode *bc, int warn, /*@null@*/ void *d);
|
||||
|
||||
/** Convert a symbol reference to its byte representation. Usually implemented
|
||||
* by object formats and debug formats to keep track of relocations generated
|
||||
* by themselves.
|
||||
* \param sym symbol
|
||||
* \param bc current bytecode (usually passed into higher-level
|
||||
* calling function)
|
||||
* \param buf buffer for byte representation
|
||||
* \param destsize destination size (in bytes)
|
||||
* \param valsize size (in bits)
|
||||
* \param warn enables standard warnings: zero for none;
|
||||
* nonzero for overflow/underflow floating point warnings;
|
||||
* negative for signed integer warnings,
|
||||
* positive for unsigned integer warnings
|
||||
* \param d objfmt-specific data (passed into higher-level calling
|
||||
* function)
|
||||
* \return Nonzero if an error occurred, 0 otherwise.
|
||||
*/
|
||||
typedef int (*yasm_output_reloc_func)
|
||||
(yasm_symrec *sym, yasm_bytecode *bc, unsigned char *buf,
|
||||
unsigned int destsize, unsigned int valsize, int warn, void *d);
|
||||
|
||||
/** Sort an array using merge sort algorithm.
|
||||
* \internal
|
||||
* \param base base of array
|
||||
* \param nmemb number of elements in array
|
||||
* \param size size of each array element
|
||||
* \param compar element comparison function
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm__mergesort(void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(const void *, const void *));
|
||||
|
||||
/** Separate string by delimiters.
|
||||
* \internal
|
||||
* \param stringp string
|
||||
* \param delim set of 1 or more delimiters
|
||||
* \return First/next substring.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@null@*/ char *yasm__strsep(char **stringp, const char *delim);
|
||||
|
||||
/** Compare two strings, ignoring case differences.
|
||||
* \internal
|
||||
* \param s1 string 1
|
||||
* \param s2 string 2
|
||||
* \return 0 if strings are equal, -1 if s1<s2, 1 if s1>s2.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm__strcasecmp(const char *s1, const char *s2);
|
||||
|
||||
/** Compare portion of two strings, ignoring case differences.
|
||||
* \internal
|
||||
* \param s1 string 1
|
||||
* \param s2 string 2
|
||||
* \param n maximum number of characters to compare
|
||||
* \return 0 if strings are equal, -1 if s1<s2, 1 if s1>s2.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm__strncasecmp(const char *s1, const char *s2, size_t n);
|
||||
|
||||
/** strdup() implementation using yasm_xmalloc().
|
||||
* \internal
|
||||
* \param str string
|
||||
* \return Newly allocated duplicate string.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ char *yasm__xstrdup(const char *str);
|
||||
|
||||
/** strndup() implementation using yasm_xmalloc().
|
||||
* \internal
|
||||
* \param str string
|
||||
* \param max maximum number of characters to copy
|
||||
* \return Newly allocated duplicate string.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ char *yasm__xstrndup(const char *str, size_t max);
|
||||
|
||||
/** Error-checking memory allocation. A default implementation is provided
|
||||
* that calls yasm_fatal() on allocation errors.
|
||||
* A replacement should \em never return NULL.
|
||||
* \param size number of bytes to allocate
|
||||
* \return Allocated memory block.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
extern /*@only@*/ /*@out@*/ void * (*yasm_xmalloc) (size_t size);
|
||||
|
||||
/** Error-checking memory allocation (with clear-to-0). A default
|
||||
* implementation is provided that calls yasm_fatal() on allocation errors.
|
||||
* A replacement should \em never return NULL.
|
||||
* \param size number of elements to allocate
|
||||
* \param elsize size (in bytes) of each element
|
||||
* \return Allocated and cleared memory block.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
extern /*@only@*/ void * (*yasm_xcalloc) (size_t nelem, size_t elsize);
|
||||
|
||||
/** Error-checking memory reallocation. A default implementation is provided
|
||||
* that calls yasm_fatal() on allocation errors. A replacement should
|
||||
* \em never return NULL.
|
||||
* \param oldmem memory block to resize
|
||||
* \param elsize new size, in bytes
|
||||
* \return Re-allocated memory block.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
extern /*@only@*/ void * (*yasm_xrealloc)
|
||||
(/*@only@*/ /*@out@*/ /*@returned@*/ /*@null@*/ void *oldmem, size_t size)
|
||||
/*@modifies oldmem@*/;
|
||||
|
||||
/** Error-checking memory deallocation. A default implementation is provided
|
||||
* that calls yasm_fatal() on allocation errors.
|
||||
* \param p memory block to free
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
extern void (*yasm_xfree) (/*@only@*/ /*@out@*/ /*@null@*/ void *p)
|
||||
/*@modifies p@*/;
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,122 @@
|
||||
/**
|
||||
* \file libyasm/dbgfmt.h
|
||||
* \brief YASM debug format interface.
|
||||
*
|
||||
* \license
|
||||
* Copyright (C) 2002-2007 Peter Johnson
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* \endlicense
|
||||
*/
|
||||
#ifndef YASM_DBGFMT_H
|
||||
#define YASM_DBGFMT_H
|
||||
|
||||
#ifndef YASM_DOXYGEN
|
||||
/** Base #yasm_dbgfmt structure. Must be present as the first element in any
|
||||
* #yasm_dbgfmt implementation.
|
||||
*/
|
||||
typedef struct yasm_dbgfmt_base {
|
||||
/** #yasm_dbgfmt_module implementation for this debug format. */
|
||||
const struct yasm_dbgfmt_module *module;
|
||||
} yasm_dbgfmt_base;
|
||||
#endif
|
||||
|
||||
/** Debug format module interface. */
|
||||
struct yasm_dbgfmt_module {
|
||||
/** One-line description of the debug format. */
|
||||
const char *name;
|
||||
|
||||
/** Keyword used to select debug format. */
|
||||
const char *keyword;
|
||||
|
||||
/** NULL-terminated list of directives. NULL if none. */
|
||||
/*@null@*/ const yasm_directive *directives;
|
||||
|
||||
/** Create debug format.
|
||||
* Module-level implementation of yasm_dbgfmt_create().
|
||||
* The filenames are provided solely for informational purposes.
|
||||
* \param object object
|
||||
* \return NULL if object format does not provide needed support.
|
||||
*/
|
||||
/*@null@*/ /*@only@*/ yasm_dbgfmt * (*create) (yasm_object *object);
|
||||
|
||||
/** Module-level implementation of yasm_dbgfmt_destroy().
|
||||
* Call yasm_dbgfmt_destroy() instead of calling this function.
|
||||
*/
|
||||
void (*destroy) (/*@only@*/ yasm_dbgfmt *dbgfmt);
|
||||
|
||||
/** Module-level implementation of yasm_dbgfmt_generate().
|
||||
* Call yasm_dbgfmt_generate() instead of calling this function.
|
||||
*/
|
||||
void (*generate) (yasm_object *object, yasm_linemap *linemap,
|
||||
yasm_errwarns *errwarns);
|
||||
};
|
||||
|
||||
/** Get the keyword used to select a debug format.
|
||||
* \param dbgfmt debug format
|
||||
* \return keyword
|
||||
*/
|
||||
const char *yasm_dbgfmt_keyword(const yasm_dbgfmt *dbgfmt);
|
||||
|
||||
/** Initialize debug output for use. Must call before any other debug
|
||||
* format functions. The filenames are provided solely for informational
|
||||
* purposes.
|
||||
* \param module debug format module
|
||||
* \param object object to generate debugging information for
|
||||
* \return NULL if object format does not provide needed support.
|
||||
*/
|
||||
/*@null@*/ /*@only@*/ yasm_dbgfmt *yasm_dbgfmt_create
|
||||
(const yasm_dbgfmt_module *module, yasm_object *object);
|
||||
|
||||
/** Cleans up any allocated debug format memory.
|
||||
* \param dbgfmt debug format
|
||||
*/
|
||||
void yasm_dbgfmt_destroy(/*@only@*/ yasm_dbgfmt *dbgfmt);
|
||||
|
||||
/** Generate debugging information bytecodes.
|
||||
* \param object object
|
||||
* \param linemap virtual/physical line mapping
|
||||
* \param errwarns error/warning set
|
||||
* \note Errors and warnings are stored into errwarns.
|
||||
*/
|
||||
void yasm_dbgfmt_generate(yasm_object *object, yasm_linemap *linemap,
|
||||
yasm_errwarns *errwarns);
|
||||
|
||||
#ifndef YASM_DOXYGEN
|
||||
|
||||
/* Inline macro implementations for dbgfmt functions */
|
||||
|
||||
#define yasm_dbgfmt_keyword(dbgfmt) \
|
||||
(((yasm_dbgfmt_base *)dbgfmt)->module->keyword)
|
||||
|
||||
#define yasm_dbgfmt_create(module, object) \
|
||||
module->create(object)
|
||||
|
||||
#define yasm_dbgfmt_destroy(dbgfmt) \
|
||||
((yasm_dbgfmt_base *)dbgfmt)->module->destroy(dbgfmt)
|
||||
#define yasm_dbgfmt_generate(object, linemap, ews) \
|
||||
((yasm_dbgfmt_base *)((object)->dbgfmt))->module->generate \
|
||||
(object, linemap, ews)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,348 @@
|
||||
/**
|
||||
* \file libyasm/errwarn.h
|
||||
* \brief YASM error and warning reporting interface.
|
||||
*
|
||||
* \license
|
||||
* Copyright (C) 2001-2007 Peter Johnson
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* \endlicense
|
||||
*/
|
||||
#ifndef YASM_ERRWARN_H
|
||||
#define YASM_ERRWARN_H
|
||||
|
||||
#ifndef YASM_LIB_DECL
|
||||
#define YASM_LIB_DECL
|
||||
#endif
|
||||
|
||||
/** Warning classes (that may be enabled/disabled). */
|
||||
typedef enum yasm_warn_class {
|
||||
YASM_WARN_NONE = 0, /**< No warning */
|
||||
YASM_WARN_GENERAL, /**< Non-specific warnings */
|
||||
YASM_WARN_UNREC_CHAR, /**< Unrecognized characters (while tokenizing) */
|
||||
YASM_WARN_PREPROC, /**< Preprocessor warnings */
|
||||
YASM_WARN_ORPHAN_LABEL, /**< Label alone on a line without a colon */
|
||||
YASM_WARN_UNINIT_CONTENTS, /**< Uninitialized space in code/data section */
|
||||
YASM_WARN_SIZE_OVERRIDE,/**< Double size override */
|
||||
YASM_WARN_IMPLICIT_SIZE_OVERRIDE /**< Implicit size override */
|
||||
} yasm_warn_class;
|
||||
|
||||
/** Error classes. Bitmask-based to support limited subclassing. */
|
||||
typedef enum yasm_error_class {
|
||||
YASM_ERROR_NONE = 0x0000, /**< No error */
|
||||
YASM_ERROR_GENERAL = 0xFFFF, /**< Non-specific */
|
||||
YASM_ERROR_ARITHMETIC = 0x0001, /**< Arithmetic error (general) */
|
||||
YASM_ERROR_OVERFLOW = 0x8001, /**< Arithmetic overflow */
|
||||
YASM_ERROR_FLOATING_POINT = 0x4001, /**< Floating point error */
|
||||
YASM_ERROR_ZERO_DIVISION = 0x2001, /**< Divide-by-zero */
|
||||
YASM_ERROR_ASSERTION = 0x0002, /**< Assertion error */
|
||||
YASM_ERROR_VALUE = 0x0004, /**< Value inappropriate
|
||||
* (e.g. not in range) */
|
||||
YASM_ERROR_NOT_ABSOLUTE = 0x8004, /**< Absolute expression required */
|
||||
YASM_ERROR_TOO_COMPLEX = 0x4004, /**< Expression too complex */
|
||||
YASM_ERROR_NOT_CONSTANT = 0x2004, /**< Constant expression required */
|
||||
YASM_ERROR_IO = 0x0008, /**< I/O error */
|
||||
YASM_ERROR_NOT_IMPLEMENTED = 0x0010, /**< Not implemented error */
|
||||
YASM_ERROR_TYPE = 0x0020, /**< Type error */
|
||||
YASM_ERROR_SYNTAX = 0x0040, /**< Syntax error */
|
||||
YASM_ERROR_PARSE = 0x8040 /**< Parser error */
|
||||
} yasm_error_class;
|
||||
|
||||
/** Initialize any internal data structures. */
|
||||
YASM_LIB_DECL
|
||||
void yasm_errwarn_initialize(void);
|
||||
|
||||
/** Clean up any memory allocated by yasm_errwarn_initialize() or other
|
||||
* functions.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_errwarn_cleanup(void);
|
||||
|
||||
/** Reporting point of internal errors. These are usually due to sanity
|
||||
* check failures in the code.
|
||||
* \warning This function must NOT return to calling code; exit or longjmp
|
||||
* instead.
|
||||
* \param file source file (ala __FILE__)
|
||||
* \param line source line (ala __LINE__)
|
||||
* \param message internal error message
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
extern /*@exits@*/ void (*yasm_internal_error_)
|
||||
(const char *file, unsigned int line, const char *message);
|
||||
|
||||
/** Easily-callable version of yasm_internal_error_(). Automatically uses
|
||||
* __FILE__ and __LINE__ as the file and line.
|
||||
* \param message internal error message
|
||||
*/
|
||||
#define yasm_internal_error(message) \
|
||||
yasm_internal_error_(__FILE__, __LINE__, message)
|
||||
|
||||
/** Reporting point of fatal errors.
|
||||
* \warning This function must NOT return to calling code; exit or longjmp
|
||||
* instead.
|
||||
* \param message fatal error message
|
||||
* \param va va_list argument list for message
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
extern /*@exits@*/ void (*yasm_fatal) (const char *message, va_list va);
|
||||
|
||||
/** Reporting point of fatal errors, with variable arguments (internal only).
|
||||
* \warning This function calls #yasm_fatal, and thus does not return to the
|
||||
* calling code.
|
||||
* \param message fatal error message
|
||||
* \param ... argument list for message
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@exits@*/ void yasm__fatal(const char *message, ...);
|
||||
|
||||
/** Unconditionally clear the error indicator, freeing any associated data.
|
||||
* Has no effect if the error indicator is not set.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_error_clear(void);
|
||||
|
||||
/** Get the error indicator. YASM_ERROR_NONE is returned if no error has
|
||||
* been set. Note that as YASM_ERROR_NONE is 0, the return value can also
|
||||
* be treated as a boolean value.
|
||||
* \return Current error indicator.
|
||||
*/
|
||||
yasm_error_class yasm_error_occurred(void);
|
||||
|
||||
/** Check the error indicator against an error class. To check if any error
|
||||
* has been set, check against the YASM_ERROR_GENERAL class. This function
|
||||
* properly checks error subclasses.
|
||||
* \param eclass base error class to check against
|
||||
* \return Nonzero if error indicator is set and a subclass of eclass, 0
|
||||
* otherwise.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_error_matches(yasm_error_class eclass);
|
||||
|
||||
#ifndef YASM_DOXYGEN
|
||||
YASM_LIB_DECL
|
||||
extern yasm_error_class yasm_eclass;
|
||||
#define yasm_error_occurred() yasm_eclass
|
||||
#endif
|
||||
|
||||
/** Set the error indicator (va_list version). Has no effect if the error
|
||||
* indicator is already set.
|
||||
* \param eclass error class
|
||||
* \param format printf format string
|
||||
* \param va argument list for format
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_error_set_va(yasm_error_class eclass, const char *format, va_list va);
|
||||
|
||||
/** Set the error indicator. Has no effect if the error indicator is already
|
||||
* set.
|
||||
* \param eclass error class
|
||||
* \param format printf format string
|
||||
* \param ... argument list for format
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_error_set(yasm_error_class eclass, const char *format, ...)
|
||||
/*@printflike@*/;
|
||||
|
||||
/** Set a cross-reference for a new error (va_list version). Has no effect
|
||||
* if the error indicator is already set (e.g. with yasm_error_set()). This
|
||||
* function must be called prior to its corresponding yasm_error_set() call.
|
||||
* \param xrefline virtual line to cross-reference to (should not be 0)
|
||||
* \param format printf format string
|
||||
* \param va argument list for format
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_error_set_xref_va(unsigned long xrefline, const char *format,
|
||||
va_list va);
|
||||
|
||||
/** Set a cross-reference for a new error. Has no effect if the error
|
||||
* indicator is already set (e.g. with yasm_error_set()). This function
|
||||
* must be called prior to its corresponding yasm_error_set() call.
|
||||
* \param xrefline virtual line to cross-reference to (should not be 0)
|
||||
* \param format printf format string
|
||||
* \param ... argument list for format
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_error_set_xref(unsigned long xrefline, const char *format, ...)
|
||||
/*@printflike@*/;
|
||||
|
||||
/** Fetch the error indicator and all associated data. If the error
|
||||
* indicator is set, the output pointers are set to the current error
|
||||
* indicator values, and the error indicator is cleared.
|
||||
* The code using this function is then responsible for yasm_xfree()'ing
|
||||
* str and xrefstr (if non-NULL). If the error indicator is not set,
|
||||
* all output values are set to 0 (including eclass, which is set to
|
||||
* YASM_ERROR_NONE).
|
||||
* \param eclass error class (output)
|
||||
* \param str error message
|
||||
* \param xrefline virtual line used for cross-referencing (0 if no xref)
|
||||
* \param xrefstr cross-reference error message (NULL if no xref)
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_error_fetch(/*@out@*/ yasm_error_class *eclass,
|
||||
/*@out@*/ /*@only@*/ /*@null@*/ char **str,
|
||||
/*@out@*/ unsigned long *xrefline,
|
||||
/*@out@*/ /*@only@*/ /*@null@*/ char **xrefstr);
|
||||
|
||||
/** Unconditionally clear all warning indicators, freeing any associated data.
|
||||
* Has no effect if no warning indicators have been set.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_warn_clear(void);
|
||||
|
||||
/** Get the first warning indicator. YASM_WARN_NONE is returned if no warning
|
||||
* has been set. Note that as YASM_WARN_NONE is 0, the return value can also
|
||||
* be treated as a boolean value.
|
||||
* \return First warning indicator.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
yasm_warn_class yasm_warn_occurred(void);
|
||||
|
||||
/** Add a warning indicator (va_list version).
|
||||
* \param wclass warning class
|
||||
* \param format printf format string
|
||||
* \param va argument list for format
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_warn_set_va(yasm_warn_class wclass, const char *format, va_list va);
|
||||
|
||||
/** Add a warning indicator.
|
||||
* \param wclass warning class
|
||||
* \param format printf format string
|
||||
* \param ... argument list for format
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_warn_set(yasm_warn_class wclass, const char *format, ...)
|
||||
/*@printflike@*/;
|
||||
|
||||
/** Fetch the first warning indicator and all associated data. If there
|
||||
* is at least one warning indicator, the output pointers are set to the
|
||||
* first warning indicator values, and first warning indicator is removed.
|
||||
* The code using this function is then responsible for yasm_xfree()'ing
|
||||
* str and xrefstr (if non-NULL). If there is no warning indicator set,
|
||||
* all output values are set to 0 (including wclass, which is set to
|
||||
* YASM_WARN_NONE).
|
||||
* \param wclass warning class (output)
|
||||
* \param str warning message
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_warn_fetch(/*@out@*/ yasm_warn_class *wclass,
|
||||
/*@out@*/ /*@only@*/ char **str);
|
||||
|
||||
/** Enable a class of warnings.
|
||||
* \param wclass warning class
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_warn_enable(yasm_warn_class wclass);
|
||||
|
||||
/** Disable a class of warnings.
|
||||
* \param wclass warning class
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_warn_disable(yasm_warn_class wclass);
|
||||
|
||||
/** Disable all classes of warnings. */
|
||||
YASM_LIB_DECL
|
||||
void yasm_warn_disable_all(void);
|
||||
|
||||
/** Create an error/warning set for collection of multiple error/warnings.
|
||||
* \return Newly allocated set.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_errwarns *yasm_errwarns_create(void);
|
||||
|
||||
/** Destroy an error/warning set.
|
||||
* \param errwarns error/warning set
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_errwarns_destroy(/*@only@*/ yasm_errwarns *errwarns);
|
||||
|
||||
/** Propagate error indicator and warning indicator(s) to an error/warning set.
|
||||
* Has no effect if the error indicator and warning indicator are not set.
|
||||
* Does not print immediately; yasm_errwarn_output_all() outputs
|
||||
* accumulated errors and warnings.
|
||||
* Generally multiple errors on the same line will be reported, but errors
|
||||
* of class YASM_ERROR_PARSE will get overwritten by any other class on the
|
||||
* same line.
|
||||
* \param errwarns error/warning set
|
||||
* \param line virtual line
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_errwarn_propagate(yasm_errwarns *errwarns, unsigned long line);
|
||||
|
||||
/** Get total number of errors logged.
|
||||
* \param errwarns error/warning set
|
||||
* \param warning_as_error if nonzero, warnings are treated as errors.
|
||||
* \return Number of errors.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
unsigned int yasm_errwarns_num_errors(yasm_errwarns *errwarns,
|
||||
int warning_as_error);
|
||||
|
||||
/** Print out an error.
|
||||
* \param fn filename of source file
|
||||
* \param line line number
|
||||
* \param msg error message
|
||||
* \param xref_fn cross-referenced source filename
|
||||
* \param xref_line cross-referenced line number
|
||||
* \param xref_msg cross-referenced error message
|
||||
*/
|
||||
typedef void (*yasm_print_error_func)
|
||||
(const char *fn, unsigned long line, const char *msg,
|
||||
/*@null@*/ const char *xref_fn, unsigned long xref_line,
|
||||
/*@null@*/ const char *xref_msg);
|
||||
|
||||
/** Print out a warning.
|
||||
* \param fn filename of source file
|
||||
* \param line line number
|
||||
* \param msg warning message
|
||||
*/
|
||||
typedef void (*yasm_print_warning_func)
|
||||
(const char *fn, unsigned long line, const char *msg);
|
||||
|
||||
/** Outputs error/warning set in sorted order (sorted by virtual line number).
|
||||
* \param errwarns error/warning set
|
||||
* \param lm line map (to convert virtual lines into filename/line pairs)
|
||||
* \param warning_as_error if nonzero, treat warnings as errors.
|
||||
* \param print_error function called to print out errors
|
||||
* \param print_warning function called to print out warnings
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_errwarns_output_all
|
||||
(yasm_errwarns *errwarns, yasm_linemap *lm, int warning_as_error,
|
||||
yasm_print_error_func print_error, yasm_print_warning_func print_warning);
|
||||
|
||||
/** Convert a possibly unprintable character into a printable string.
|
||||
* \internal
|
||||
* \param ch possibly unprintable character
|
||||
* \return Printable string representation (static buffer).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
char *yasm__conv_unprint(int ch);
|
||||
|
||||
/** Hook for library users to map to gettext() if GNU gettext is being used.
|
||||
* \param msgid message catalog identifier
|
||||
* \return Translated message.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
extern const char * (*yasm_gettext_hook) (const char *msgid);
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,388 @@
|
||||
/**
|
||||
* \file libyasm/expr.h
|
||||
* \brief YASM expression interface.
|
||||
*
|
||||
* \license
|
||||
* Copyright (C) 2001-2007 Michael Urman, Peter Johnson
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* \endlicense
|
||||
*/
|
||||
#ifndef YASM_EXPR_H
|
||||
#define YASM_EXPR_H
|
||||
|
||||
#ifndef YASM_LIB_DECL
|
||||
#define YASM_LIB_DECL
|
||||
#endif
|
||||
|
||||
/** Type of an expression item. Types are listed in canonical sorting order.
|
||||
* See expr_order_terms().
|
||||
* Note #YASM_EXPR_PRECBC must be used carefully (in a-b pairs), as only
|
||||
* symrecs can become the relative term in a #yasm_value.
|
||||
*/
|
||||
typedef enum yasm_expr__type {
|
||||
YASM_EXPR_NONE = 0, /**< Nothing */
|
||||
YASM_EXPR_REG = 1<<0, /**< Register */
|
||||
YASM_EXPR_INT = 1<<1, /**< Integer value */
|
||||
YASM_EXPR_SUBST = 1<<2, /**< Substitution placeholder */
|
||||
YASM_EXPR_FLOAT = 1<<3, /**< Floating point value */
|
||||
YASM_EXPR_SYM = 1<<4, /**< Symbol */
|
||||
YASM_EXPR_PRECBC = 1<<5,/**< Direct bytecode ref (rather than via sym) */
|
||||
YASM_EXPR_EXPR = 1<<6 /**< Subexpression */
|
||||
} yasm_expr__type;
|
||||
|
||||
/** Expression item. */
|
||||
typedef struct yasm_expr__item {
|
||||
yasm_expr__type type; /**< Type */
|
||||
|
||||
/** Expression item data. Correct value depends on type. */
|
||||
union {
|
||||
yasm_bytecode *precbc; /**< Direct bytecode ref (YASM_EXPR_PRECBC) */
|
||||
yasm_symrec *sym; /**< Symbol (YASM_EXPR_SYM) */
|
||||
yasm_expr *expn; /**< Subexpression (YASM_EXPR_EXPR) */
|
||||
yasm_intnum *intn; /**< Integer value (YASM_EXPR_INT) */
|
||||
yasm_floatnum *flt; /**< Floating point value (YASM_EXPR_FLOAT) */
|
||||
uintptr_t reg; /**< Register (YASM_EXPR_REG) */
|
||||
unsigned int subst; /**< Subst placeholder (YASM_EXPR_SUBST) */
|
||||
} data;
|
||||
} yasm_expr__item;
|
||||
|
||||
/** Expression. */
|
||||
struct yasm_expr {
|
||||
yasm_expr_op op; /**< Operation. */
|
||||
unsigned long line; /**< Line number where expression was defined. */
|
||||
int numterms; /**< Number of terms in the expression. */
|
||||
|
||||
/** Terms of the expression. Structure may be extended to include more
|
||||
* terms, as some operations may allow more than two operand terms
|
||||
* (ADD, MUL, OR, AND, XOR).
|
||||
*/
|
||||
yasm_expr__item terms[2];
|
||||
};
|
||||
|
||||
/** Create a new expression e=a op b.
|
||||
* \param op operation
|
||||
* \param a expression item a
|
||||
* \param b expression item b (optional depending on op)
|
||||
* \param line virtual line (where expression defined)
|
||||
* \return Newly allocated expression.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_expr *yasm_expr_create
|
||||
(yasm_expr_op op, /*@only@*/ yasm_expr__item *a,
|
||||
/*@only@*/ /*@null@*/ yasm_expr__item *b, unsigned long line);
|
||||
|
||||
/** Create a new preceding-bytecode expression item.
|
||||
* \param precbc preceding bytecode
|
||||
* \return Newly allocated expression item.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_expr__item *yasm_expr_precbc(/*@keep@*/ yasm_bytecode *precbc);
|
||||
|
||||
/** Create a new symbol expression item.
|
||||
* \param sym symbol
|
||||
* \return Newly allocated expression item.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_expr__item *yasm_expr_sym(/*@keep@*/ yasm_symrec *sym);
|
||||
|
||||
/** Create a new expression expression item.
|
||||
* \param e expression
|
||||
* \return Newly allocated expression item.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_expr__item *yasm_expr_expr(/*@keep@*/ yasm_expr *e);
|
||||
|
||||
/** Create a new intnum expression item.
|
||||
* \param intn intnum
|
||||
* \return Newly allocated expression item.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_expr__item *yasm_expr_int(/*@keep@*/ yasm_intnum *intn);
|
||||
|
||||
/** Create a new floatnum expression item.
|
||||
* \param flt floatnum
|
||||
* \return Newly allocated expression item.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_expr__item *yasm_expr_float(/*@keep@*/ yasm_floatnum *flt);
|
||||
|
||||
/** Create a new register expression item.
|
||||
* \param reg register
|
||||
* \return Newly allocated expression item.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ yasm_expr__item *yasm_expr_reg(uintptr_t reg);
|
||||
|
||||
/** Create a new expression tree e=l op r.
|
||||
* \param l expression for left side of new expression
|
||||
* \param o operation
|
||||
* \param r expression for right side of new expression
|
||||
* \param i line index
|
||||
* \return Newly allocated expression.
|
||||
*/
|
||||
#define yasm_expr_create_tree(l,o,r,i) \
|
||||
yasm_expr_create ((o), yasm_expr_expr(l), yasm_expr_expr(r), i)
|
||||
|
||||
/** Create a new expression branch e=op r.
|
||||
* \param o operation
|
||||
* \param r expression for right side of new expression
|
||||
* \param i line index
|
||||
* \return Newly allocated expression.
|
||||
*/
|
||||
#define yasm_expr_create_branch(o,r,i) \
|
||||
yasm_expr_create ((o), yasm_expr_expr(r), (yasm_expr__item *)NULL, i)
|
||||
|
||||
/** Create a new expression identity e=r.
|
||||
* \param r expression for identity within new expression
|
||||
* \param i line index
|
||||
* \return Newly allocated expression.
|
||||
*/
|
||||
#define yasm_expr_create_ident(r,i) \
|
||||
yasm_expr_create (YASM_EXPR_IDENT, (r), (yasm_expr__item *)NULL, i)
|
||||
|
||||
/** Duplicate an expression.
|
||||
* \param e expression
|
||||
* \return Newly allocated expression identical to e.
|
||||
*/
|
||||
yasm_expr *yasm_expr_copy(const yasm_expr *e);
|
||||
#ifndef YASM_DOXYGEN
|
||||
#define yasm_expr_copy(e) yasm_expr__copy_except(e, -1)
|
||||
#endif
|
||||
|
||||
/** Destroy (free allocated memory for) an expression.
|
||||
* \param e expression
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_expr_destroy(/*@only@*/ /*@null@*/ yasm_expr *e);
|
||||
|
||||
/** Determine if an expression is a specified operation (at the top level).
|
||||
* \param e expression
|
||||
* \param op operator
|
||||
* \return Nonzero if the expression was the specified operation at the top
|
||||
* level, zero otherwise.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_expr_is_op(const yasm_expr *e, yasm_expr_op op);
|
||||
|
||||
/** Extra transformation function for yasm_expr__level_tree().
|
||||
* \param e expression being simplified
|
||||
* \param d data provided as expr_xform_extra_data to
|
||||
* yasm_expr__level_tree()
|
||||
* \return Transformed e.
|
||||
*/
|
||||
typedef /*@only@*/ yasm_expr * (*yasm_expr_xform_func)
|
||||
(/*@returned@*/ /*@only@*/ yasm_expr *e, /*@null@*/ void *d);
|
||||
|
||||
/** Level an entire expression tree.
|
||||
* \internal
|
||||
* \param e expression
|
||||
* \param fold_const enable constant folding if nonzero
|
||||
* \param simplify_ident simplify identities
|
||||
* \param simplify_reg_mul simplify REG*1 identities
|
||||
* \param calc_bc_dist nonzero if distances between bytecodes should be
|
||||
* calculated, 0 if they should be left intact
|
||||
* \param expr_xform_extra extra transformation function
|
||||
* \param expr_xform_extra_data data to pass to expr_xform_extra
|
||||
* \return Leveled expression.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ /*@null@*/ yasm_expr *yasm_expr__level_tree
|
||||
(/*@returned@*/ /*@only@*/ /*@null@*/ yasm_expr *e, int fold_const,
|
||||
int simplify_ident, int simplify_reg_mul, int calc_bc_dist,
|
||||
/*@null@*/ yasm_expr_xform_func expr_xform_extra,
|
||||
/*@null@*/ void *expr_xform_extra_data);
|
||||
|
||||
/** Simplify an expression as much as possible. Eliminates extraneous
|
||||
* branches and simplifies integer-only subexpressions. Simplified version
|
||||
* of yasm_expr__level_tree().
|
||||
* \param e expression
|
||||
* \param cbd if distance between bytecodes should be calculated
|
||||
* \return Simplified expression.
|
||||
*/
|
||||
#define yasm_expr_simplify(e, cbd) \
|
||||
yasm_expr__level_tree(e, 1, 1, 1, cbd, NULL, NULL)
|
||||
|
||||
/** Extract the segment portion of an expression containing SEG:OFF, leaving
|
||||
* the offset.
|
||||
* \param ep expression (pointer to)
|
||||
* \return NULL if unable to extract a segment (expr does not contain a
|
||||
* YASM_EXPR_SEGOFF operator), otherwise the segment expression.
|
||||
* The input expression is modified such that on return, it's the
|
||||
* offset expression.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ /*@null@*/ yasm_expr *yasm_expr_extract_deep_segoff(yasm_expr **ep);
|
||||
|
||||
/** Extract the segment portion of a SEG:OFF expression, leaving the offset.
|
||||
* \param ep expression (pointer to)
|
||||
* \return NULL if unable to extract a segment (YASM_EXPR_SEGOFF not the
|
||||
* top-level operator), otherwise the segment expression. The input
|
||||
* expression is modified such that on return, it's the offset
|
||||
* expression.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ /*@null@*/ yasm_expr *yasm_expr_extract_segoff(yasm_expr **ep);
|
||||
|
||||
/** Extract the right portion (y) of a x WRT y expression, leaving the left
|
||||
* portion (x).
|
||||
* \param ep expression (pointer to)
|
||||
* \return NULL if unable to extract (YASM_EXPR_WRT not the top-level
|
||||
* operator), otherwise the right side of the WRT expression. The
|
||||
* input expression is modified such that on return, it's the left side
|
||||
* of the WRT expression.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ /*@null@*/ yasm_expr *yasm_expr_extract_wrt(yasm_expr **ep);
|
||||
|
||||
/** Get the integer value of an expression if it's just an integer.
|
||||
* \param ep expression (pointer to)
|
||||
* \param calc_bc_dist nonzero if distances between bytecodes should be
|
||||
* calculated, 0 if NULL should be returned in this case
|
||||
* \return NULL if the expression is too complex (contains anything other than
|
||||
* integers, ie floats, non-valued labels, registers); otherwise the
|
||||
* intnum value of the expression.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@dependent@*/ /*@null@*/ yasm_intnum *yasm_expr_get_intnum
|
||||
(yasm_expr **ep, int calc_bc_dist);
|
||||
|
||||
/** Get the symbol value of an expression if it's just a symbol.
|
||||
* \param ep expression (pointer to)
|
||||
* \param simplify if nonzero, simplify the expression first
|
||||
* \return NULL if the expression is too complex; otherwise the symbol value of
|
||||
* the expression.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@dependent@*/ /*@null@*/ const yasm_symrec *yasm_expr_get_symrec
|
||||
(yasm_expr **ep, int simplify);
|
||||
|
||||
/** Get the register value of an expression if it's just a register.
|
||||
* \param ep expression (pointer to)
|
||||
* \param simplify if nonzero, simplify the expression first
|
||||
* \return NULL if the expression is too complex; otherwise the register value
|
||||
* of the expression.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@dependent@*/ /*@null@*/ const uintptr_t *yasm_expr_get_reg
|
||||
(yasm_expr **ep, int simplify);
|
||||
|
||||
/** Print an expression. For debugging purposes.
|
||||
* \param e expression
|
||||
* \param f file
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_expr_print(/*@null@*/ const yasm_expr *e, FILE *f);
|
||||
|
||||
/** Return the size of an expression, if the user provided it
|
||||
* \param e expression
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
unsigned int yasm_expr_size(const yasm_expr *e);
|
||||
|
||||
/** Return the segment of an expression, if the user provided it
|
||||
* \param e expression
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
const char *yasm_expr_segment(const yasm_expr *e);
|
||||
|
||||
/** Traverse over expression tree in order (const version).
|
||||
* Calls func for each leaf (non-operation).
|
||||
* \param e expression
|
||||
* \param d data passed to each call to func
|
||||
* \param func callback function
|
||||
* \return Stops early (and returns 1) if func returns 1.
|
||||
* Otherwise returns 0.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_expr__traverse_leaves_in_const
|
||||
(const yasm_expr *e, /*@null@*/ void *d,
|
||||
int (*func) (/*@null@*/ const yasm_expr__item *ei, /*@null@*/ void *d));
|
||||
|
||||
/** Traverse over expression tree in order.
|
||||
* Calls func for each leaf (non-operation).
|
||||
* \param e expression
|
||||
* \param d data passed to each call to func
|
||||
* \param func callback function
|
||||
* \return Stops early (and returns 1) if func returns 1.
|
||||
* Otherwise returns 0.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_expr__traverse_leaves_in
|
||||
(yasm_expr *e, /*@null@*/ void *d,
|
||||
int (*func) (/*@null@*/ yasm_expr__item *ei, /*@null@*/ void *d));
|
||||
|
||||
/** Reorder terms of e into canonical order. Only reorders if reordering
|
||||
* doesn't change meaning of expression. (eg, doesn't reorder SUB).
|
||||
* Canonical order: REG, INT, FLOAT, SYM, EXPR.
|
||||
* Multiple terms of a single type are kept in the same order as in
|
||||
* the original expression.
|
||||
* \param e expression
|
||||
* \note Only performs reordering on *one* level (no recursion).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_expr__order_terms(yasm_expr *e);
|
||||
|
||||
/** Copy entire expression EXCEPT for index "except" at *top level only*.
|
||||
* \param e expression
|
||||
* \param except term index not to copy; -1 to copy all terms
|
||||
* \return Newly allocated copy of expression.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
yasm_expr *yasm_expr__copy_except(const yasm_expr *e, int except);
|
||||
|
||||
/** Test if expression contains an item. Searches recursively into
|
||||
* subexpressions.
|
||||
* \param e expression
|
||||
* \param t type of item to look for
|
||||
* \return Nonzero if expression contains an item of type t, zero if not.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_expr__contains(const yasm_expr *e, yasm_expr__type t);
|
||||
|
||||
/** Transform symrec-symrec terms in expression into #YASM_EXPR_SUBST items.
|
||||
* Calls the callback function for each symrec-symrec term.
|
||||
* \param ep expression (pointer to)
|
||||
* \param cbd callback data passed to callback function
|
||||
* \param callback callback function: given subst index for bytecode
|
||||
* pair, bytecode pair (bc2-bc1), and cbd (callback data)
|
||||
* \return Number of transformations made.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_expr__bc_dist_subst(yasm_expr **ep, void *cbd,
|
||||
void (*callback) (unsigned int subst,
|
||||
yasm_bytecode *precbc,
|
||||
yasm_bytecode *precbc2,
|
||||
void *cbd));
|
||||
|
||||
/** Substitute items into expr YASM_EXPR_SUBST items (by index). Items are
|
||||
* copied, so caller is responsible for freeing array of items.
|
||||
* \param e expression
|
||||
* \param num_items number of items in items array
|
||||
* \param items items array
|
||||
* \return 1 on error (index out of range).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_expr__subst(yasm_expr *e, unsigned int num_items,
|
||||
const yasm_expr__item *items);
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,525 @@
|
||||
/**
|
||||
* \file libyasm/file.h
|
||||
* \brief YASM file helpers.
|
||||
*
|
||||
* \license
|
||||
* Copyright (C) 2001-2007 Peter Johnson
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* \endlicense
|
||||
*/
|
||||
#ifndef YASM_FILE_H
|
||||
#define YASM_FILE_H
|
||||
|
||||
#ifndef YASM_LIB_DECL
|
||||
#define YASM_LIB_DECL
|
||||
#endif
|
||||
|
||||
/** Re2c scanner state. */
|
||||
typedef struct yasm_scanner {
|
||||
unsigned char *bot; /**< Bottom of scan buffer */
|
||||
unsigned char *tok; /**< Start of token */
|
||||
unsigned char *ptr; /**< Scan marker */
|
||||
unsigned char *cur; /**< Cursor (1 past end of token) */
|
||||
unsigned char *lim; /**< Limit of good data */
|
||||
unsigned char *top; /**< Top of scan buffer */
|
||||
unsigned char *eof; /**< End of file */
|
||||
} yasm_scanner;
|
||||
|
||||
/** Initialize scanner state.
|
||||
* \param scanner Re2c scanner state
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_scanner_initialize(yasm_scanner *scanner);
|
||||
|
||||
/** Frees any memory used by scanner state; does not free state itself.
|
||||
* \param scanner Re2c scanner state
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_scanner_delete(yasm_scanner *scanner);
|
||||
|
||||
/** Fill a scanner state structure with data coming from an input function.
|
||||
* \param scanner Re2c scanner state
|
||||
* \param cursor Re2c scan cursor
|
||||
* \param input_func Input function to read data; takes buffer and maximum
|
||||
* number of bytes, returns number of bytes read.
|
||||
* \param input_func_data Data to pass as the first parameter to input_func
|
||||
* \return 1 if this was the first time this function was called on this
|
||||
* scanner state, 0 otherwise.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
int yasm_fill_helper
|
||||
(yasm_scanner *scanner, unsigned char **cursor,
|
||||
size_t (*input_func) (void *d, unsigned char *buf, size_t max),
|
||||
void *input_func_data);
|
||||
|
||||
/** Unescape a string with C-style escapes. Handles b, f, n, r, t, and hex
|
||||
* and octal escapes. String is updated in-place.
|
||||
* Edge cases:
|
||||
* - hex escapes: reads as many hex digits as possible, takes last 2 as value.
|
||||
* - oct escapes: takes up to 3 digits 0-9 and scales appropriately, with
|
||||
* warning.
|
||||
* \param str C-style string (updated in place)
|
||||
* \param len length of string (updated with new length)
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_unescape_cstring(unsigned char *str, size_t *len);
|
||||
|
||||
/** Split a UNIX pathname into head (directory) and tail (base filename)
|
||||
* portions.
|
||||
* \internal
|
||||
* \param path pathname
|
||||
* \param tail (returned) base filename
|
||||
* \return Length of head (directory).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
size_t yasm__splitpath_unix(const char *path, /*@out@*/ const char **tail);
|
||||
|
||||
/** Split a Windows pathname into head (directory) and tail (base filename)
|
||||
* portions.
|
||||
* \internal
|
||||
* \param path pathname
|
||||
* \param tail (returned) base filename
|
||||
* \return Length of head (directory).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
size_t yasm__splitpath_win(const char *path, /*@out@*/ const char **tail);
|
||||
|
||||
/** Split a pathname into head (directory) and tail (base filename) portions.
|
||||
* Unless otherwise defined, defaults to yasm__splitpath_unix().
|
||||
* \internal
|
||||
* \param path pathname
|
||||
* \param tail (returned) base filename
|
||||
* \return Length of head (directory).
|
||||
*/
|
||||
#ifndef yasm__splitpath
|
||||
# if defined (_WIN32) || defined (WIN32) || defined (__MSDOS__) || \
|
||||
defined (__DJGPP__) || defined (__OS2__)
|
||||
# define yasm__splitpath(path, tail) yasm__splitpath_win(path, tail)
|
||||
# else
|
||||
# define yasm__splitpath(path, tail) yasm__splitpath_unix(path, tail)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/** Get the current working directory.
|
||||
* \internal
|
||||
* \return Current working directory pathname (newly allocated).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ char *yasm__getcwd(void);
|
||||
|
||||
/** Convert a relative or absolute pathname into an absolute pathname.
|
||||
* \internal
|
||||
* \param path pathname
|
||||
* \return Absolute version of path (newly allocated).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@only@*/ char *yasm__abspath(const char *path);
|
||||
|
||||
/** Build a UNIX pathname that is equivalent to accessing the "to" pathname
|
||||
* when you're in the directory containing "from". Result is relative if both
|
||||
* from and to are relative.
|
||||
* \internal
|
||||
* \param from from pathname
|
||||
* \param to to pathname
|
||||
* \return Combined path (newly allocated).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
char *yasm__combpath_unix(const char *from, const char *to);
|
||||
|
||||
/** Build a Windows pathname that is equivalent to accessing the "to" pathname
|
||||
* when you're in the directory containing "from". Result is relative if both
|
||||
* from and to are relative.
|
||||
* \internal
|
||||
* \param from from pathname
|
||||
* \param to to pathname
|
||||
* \return Combined path (newly allocated).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
char *yasm__combpath_win(const char *from, const char *to);
|
||||
|
||||
/** Build a pathname that is equivalent to accessing the "to" pathname
|
||||
* when you're in the directory containing "from". Result is relative if both
|
||||
* from and to are relative.
|
||||
* Unless otherwise defined, defaults to yasm__combpath_unix().
|
||||
* \internal
|
||||
* \param from from pathname
|
||||
* \param to to pathname
|
||||
* \return Combined path (newly allocated).
|
||||
*/
|
||||
#ifndef yasm__combpath
|
||||
# if defined (_WIN32) || defined (WIN32) || defined (__MSDOS__) || \
|
||||
defined (__DJGPP__) || defined (__OS2__)
|
||||
# define yasm__combpath(from, to) yasm__combpath_win(from, to)
|
||||
# else
|
||||
# define yasm__combpath(from, to) yasm__combpath_unix(from, to)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/** Recursively create tree of directories needed for pathname.
|
||||
* \internal
|
||||
* \param path pathname
|
||||
* \param win handle windows paths
|
||||
* \return Length of directory portion of pathname.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
size_t yasm__createpath_common(const char *path, int win);
|
||||
|
||||
/** Recursively create tree of directories needed for pathname.
|
||||
* Unless otherwise defined, defaults to yasm__createpath_unix().
|
||||
* \internal
|
||||
* \param path pathname
|
||||
* \return Length of directory portion of pathname.
|
||||
*/
|
||||
#ifndef yasm__createpath
|
||||
# if defined (_WIN32) || defined (WIN32) || defined (__MSDOS__) || \
|
||||
defined (__DJGPP__) || defined (__OS2__)
|
||||
# define yasm__createpath(path) yasm__createpath_common(path, 1)
|
||||
# else
|
||||
# define yasm__createpath(path) yasm__createpath_common(path, 0)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/** Try to find and open an include file, searching through include paths.
|
||||
* First iname is looked for relative to the directory containing "from", then
|
||||
* it's looked for relative to each of the include paths.
|
||||
*
|
||||
* All pathnames may be either absolute or relative; from, oname, and
|
||||
* include paths, if relative, are relative from the current working directory.
|
||||
*
|
||||
* First match wins; the full pathname (newly allocated) to the opened file
|
||||
* is saved into oname, and the fopen'ed FILE * is returned. If not found,
|
||||
* NULL is returned.
|
||||
*
|
||||
* \param iname file to include
|
||||
* \param from file doing the including
|
||||
* \param mode fopen mode string
|
||||
* \param oname full pathname of included file (may be relative). NULL
|
||||
* may be passed if this is unwanted.
|
||||
* \return fopen'ed include file, or NULL if not found.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
/*@null@*/ FILE *yasm_fopen_include
|
||||
(const char *iname, const char *from, const char *mode,
|
||||
/*@null@*/ /*@out@*/ /*@only@*/ char **oname);
|
||||
|
||||
/** Delete any stored include paths added by yasm_add_include_path().
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_delete_include_paths(void);
|
||||
|
||||
/** Iterate through include paths.
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
const char * yasm_get_include_dir(void **iter);
|
||||
|
||||
/** Add an include path for use by yasm_fopen_include().
|
||||
* If path is relative, it is treated by yasm_fopen_include() as relative to
|
||||
* the current working directory.
|
||||
*
|
||||
* \param path path to add
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
void yasm_add_include_path(const char *path);
|
||||
|
||||
/** Write an 8-bit value to a buffer, incrementing buffer pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 8-bit value
|
||||
*/
|
||||
#define YASM_WRITE_8(ptr, val) \
|
||||
*((ptr)++) = (unsigned char)((val) & 0xFF)
|
||||
|
||||
/** Write a 16-bit value to a buffer in little endian, incrementing buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 16-bit value
|
||||
*/
|
||||
#define YASM_WRITE_16_L(ptr, val) \
|
||||
do { \
|
||||
*((ptr)++) = (unsigned char)((val) & 0xFF); \
|
||||
*((ptr)++) = (unsigned char)(((val) >> 8) & 0xFF); \
|
||||
} while (0)
|
||||
|
||||
/** Write a 32-bit value to a buffer in little endian, incrementing buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 32-bit value
|
||||
*/
|
||||
#define YASM_WRITE_32_L(ptr, val) \
|
||||
do { \
|
||||
*((ptr)++) = (unsigned char)((val) & 0xFF); \
|
||||
*((ptr)++) = (unsigned char)(((val) >> 8) & 0xFF); \
|
||||
*((ptr)++) = (unsigned char)(((val) >> 16) & 0xFF); \
|
||||
*((ptr)++) = (unsigned char)(((val) >> 24) & 0xFF); \
|
||||
} while (0)
|
||||
|
||||
/** Write a 16-bit value to a buffer in big endian, incrementing buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 16-bit value
|
||||
*/
|
||||
#define YASM_WRITE_16_B(ptr, val) \
|
||||
do { \
|
||||
*((ptr)++) = (unsigned char)(((val) >> 8) & 0xFF); \
|
||||
*((ptr)++) = (unsigned char)((val) & 0xFF); \
|
||||
} while (0)
|
||||
|
||||
/** Write a 32-bit value to a buffer in big endian, incrementing buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 32-bit value
|
||||
*/
|
||||
#define YASM_WRITE_32_B(ptr, val) \
|
||||
do { \
|
||||
*((ptr)++) = (unsigned char)(((val) >> 24) & 0xFF); \
|
||||
*((ptr)++) = (unsigned char)(((val) >> 16) & 0xFF); \
|
||||
*((ptr)++) = (unsigned char)(((val) >> 8) & 0xFF); \
|
||||
*((ptr)++) = (unsigned char)((val) & 0xFF); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/** Write an 8-bit value to a buffer. Does not increment buffer pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 8-bit value
|
||||
*/
|
||||
#define YASM_SAVE_8(ptr, val) \
|
||||
*(ptr) = (unsigned char)((val) & 0xFF)
|
||||
|
||||
/** Write a 16-bit value to a buffer in little endian. Does not increment
|
||||
* buffer pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 16-bit value
|
||||
*/
|
||||
#define YASM_SAVE_16_L(ptr, val) \
|
||||
do { \
|
||||
*(ptr) = (unsigned char)((val) & 0xFF); \
|
||||
*((ptr)+1) = (unsigned char)(((val) >> 8) & 0xFF); \
|
||||
} while (0)
|
||||
|
||||
/** Write a 32-bit value to a buffer in little endian. Does not increment
|
||||
* buffer pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 32-bit value
|
||||
*/
|
||||
#define YASM_SAVE_32_L(ptr, val) \
|
||||
do { \
|
||||
*(ptr) = (unsigned char)((val) & 0xFF); \
|
||||
*((ptr)+1) = (unsigned char)(((val) >> 8) & 0xFF); \
|
||||
*((ptr)+2) = (unsigned char)(((val) >> 16) & 0xFF); \
|
||||
*((ptr)+3) = (unsigned char)(((val) >> 24) & 0xFF); \
|
||||
} while (0)
|
||||
|
||||
/** Write a 16-bit value to a buffer in big endian. Does not increment buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 16-bit value
|
||||
*/
|
||||
#define YASM_SAVE_16_B(ptr, val) \
|
||||
do { \
|
||||
*(ptr) = (unsigned char)(((val) >> 8) & 0xFF); \
|
||||
*((ptr)+1) = (unsigned char)((val) & 0xFF); \
|
||||
} while (0)
|
||||
|
||||
/** Write a 32-bit value to a buffer in big endian. Does not increment buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 32-bit value
|
||||
*/
|
||||
#define YASM_SAVE_32_B(ptr, val) \
|
||||
do { \
|
||||
*(ptr) = (unsigned char)(((val) >> 24) & 0xFF); \
|
||||
*((ptr)+1) = (unsigned char)(((val) >> 16) & 0xFF); \
|
||||
*((ptr)+2) = (unsigned char)(((val) >> 8) & 0xFF); \
|
||||
*((ptr)+3) = (unsigned char)((val) & 0xFF); \
|
||||
} while (0)
|
||||
|
||||
/** Direct-to-file version of YASM_SAVE_16_L().
|
||||
* \note Using the macro multiple times with a single fwrite() call will
|
||||
* probably be faster than calling this function many times.
|
||||
* \param val 16-bit value
|
||||
* \param f file
|
||||
* \return 1 if the write was successful, 0 if not (just like fwrite()).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
size_t yasm_fwrite_16_l(unsigned short val, FILE *f);
|
||||
|
||||
/** Direct-to-file version of YASM_SAVE_32_L().
|
||||
* \note Using the macro multiple times with a single fwrite() call will
|
||||
* probably be faster than calling this function many times.
|
||||
* \param val 32-bit value
|
||||
* \param f file
|
||||
* \return 1 if the write was successful, 0 if not (just like fwrite()).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
size_t yasm_fwrite_32_l(unsigned long val, FILE *f);
|
||||
|
||||
/** Direct-to-file version of YASM_SAVE_16_B().
|
||||
* \note Using the macro multiple times with a single fwrite() call will
|
||||
* probably be faster than calling this function many times.
|
||||
* \param val 16-bit value
|
||||
* \param f file
|
||||
* \return 1 if the write was successful, 0 if not (just like fwrite()).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
size_t yasm_fwrite_16_b(unsigned short val, FILE *f);
|
||||
|
||||
/** Direct-to-file version of YASM_SAVE_32_B().
|
||||
* \note Using the macro multiple times with a single fwrite() call will
|
||||
* probably be faster than calling this function many times.
|
||||
* \param val 32-bit value
|
||||
* \param f file
|
||||
* \return 1 if the write was successful, 0 if not (just like fwrite()).
|
||||
*/
|
||||
YASM_LIB_DECL
|
||||
size_t yasm_fwrite_32_b(unsigned long val, FILE *f);
|
||||
|
||||
/** Read an 8-bit value from a buffer, incrementing buffer pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 8-bit value
|
||||
*/
|
||||
#define YASM_READ_8(val, ptr) \
|
||||
(val) = *((ptr)++) & 0xFF
|
||||
|
||||
/** Read a 16-bit value from a buffer in little endian, incrementing buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 16-bit value
|
||||
*/
|
||||
#define YASM_READ_16_L(val, ptr) \
|
||||
do { \
|
||||
(val) = *((ptr)++) & 0xFF; \
|
||||
(val) |= (*((ptr)++) & 0xFF) << 8; \
|
||||
} while (0)
|
||||
|
||||
/** Read a 32-bit value from a buffer in little endian, incrementing buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 32-bit value
|
||||
*/
|
||||
#define YASM_READ_32_L(val, ptr) \
|
||||
do { \
|
||||
(val) = *((ptr)++) & 0xFF; \
|
||||
(val) |= (*((ptr)++) & 0xFF) << 8; \
|
||||
(val) |= (*((ptr)++) & 0xFF) << 16; \
|
||||
(val) |= (*((ptr)++) & 0xFF) << 24; \
|
||||
} while (0)
|
||||
|
||||
/** Read a 16-bit value from a buffer in big endian, incrementing buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 16-bit value
|
||||
*/
|
||||
#define YASM_READ_16_B(val, ptr) \
|
||||
do { \
|
||||
(val) = (*((ptr)++) & 0xFF) << 8; \
|
||||
(val) |= *((ptr)++) & 0xFF; \
|
||||
} while (0)
|
||||
|
||||
/** Read a 32-bit value from a buffer in big endian, incrementing buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 32-bit value
|
||||
*/
|
||||
#define YASM_READ_32_B(val, ptr) \
|
||||
do { \
|
||||
(val) = (*((ptr)++) & 0xFF) << 24; \
|
||||
(val) |= (*((ptr)++) & 0xFF) << 16; \
|
||||
(val) |= (*((ptr)++) & 0xFF) << 8; \
|
||||
(val) |= *((ptr)++) & 0xFF; \
|
||||
} while (0)
|
||||
|
||||
/** Read an 8-bit value from a buffer. Does not increment buffer pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 8-bit value
|
||||
*/
|
||||
#define YASM_LOAD_8(val, ptr) \
|
||||
(val) = *(ptr) & 0xFF
|
||||
|
||||
/** Read a 16-bit value from a buffer in little endian. Does not increment
|
||||
* buffer pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 16-bit value
|
||||
*/
|
||||
#define YASM_LOAD_16_L(val, ptr) \
|
||||
do { \
|
||||
(val) = *(ptr) & 0xFF; \
|
||||
(val) |= (*((ptr)+1) & 0xFF) << 8; \
|
||||
} while (0)
|
||||
|
||||
/** Read a 32-bit value from a buffer in little endian. Does not increment
|
||||
* buffer pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 32-bit value
|
||||
*/
|
||||
#define YASM_LOAD_32_L(val, ptr) \
|
||||
do { \
|
||||
(val) = (unsigned long)(*(ptr) & 0xFF); \
|
||||
(val) |= (unsigned long)((*((ptr)+1) & 0xFF) << 8); \
|
||||
(val) |= (unsigned long)((*((ptr)+2) & 0xFF) << 16); \
|
||||
(val) |= (unsigned long)((*((ptr)+3) & 0xFF) << 24); \
|
||||
} while (0)
|
||||
|
||||
/** Read a 16-bit value from a buffer in big endian. Does not increment buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 16-bit value
|
||||
*/
|
||||
#define YASM_LOAD_16_B(val, ptr) \
|
||||
do { \
|
||||
(val) = (*(ptr) & 0xFF) << 8; \
|
||||
(val) |= *((ptr)+1) & 0xFF; \
|
||||
} while (0)
|
||||
|
||||
/** Read a 32-bit value from a buffer in big endian. Does not increment buffer
|
||||
* pointer.
|
||||
* \note Only works properly if ptr is an (unsigned char *).
|
||||
* \param ptr buffer
|
||||
* \param val 32-bit value
|
||||
*/
|
||||
#define YASM_LOAD_32_B(val, ptr) \
|
||||
do { \
|
||||
(val) = (unsigned long)((*(ptr) & 0xFF) << 24); \
|
||||
(val) |= (unsigned long)((*((ptr)+1) & 0xFF) << 16); \
|
||||
(val) |= (unsigned long)((*((ptr)+2) & 0xFF) << 8); \
|
||||
(val) |= (unsigned long)(*((ptr)+3) & 0xFF); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||