001/*
002 * Copyright (C) Photon Vision.
003 *
004 * This program is free software: you can redistribute it and/or modify
005 * it under the terms of the GNU General Public License as published by
006 * the Free Software Foundation, either version 3 of the License, or
007 * (at your option) any later version.
008 *
009 * This program is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012 * GNU General Public License for more details.
013 *
014 * You should have received a copy of the GNU General Public License
015 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
016 */
017
018package org.photonvision.jni;
019
020import edu.wpi.first.util.RuntimeLoader;
021import java.io.File;
022import java.io.FileOutputStream;
023import java.io.IOException;
024import java.nio.file.Files;
025import java.util.List;
026import org.photonvision.common.hardware.Platform;
027
028public class PhotonTargetingJniLoader {
029    public static boolean isWorking = false;
030
031    public static boolean load() throws IOException, UnsatisfiedLinkError {
032        if (isWorking) return true;
033        isWorking = load_();
034        return isWorking;
035    }
036
037    public static boolean load_() throws IOException, UnsatisfiedLinkError {
038        // We always extract the shared object (we could hash each so, but that's a lot
039        // of work)
040        String arch_name = Platform.getNativeLibraryFolderName();
041        var clazz = PhotonTargetingJniLoader.class;
042
043        for (var libraryName : List.of("photontargeting", "photontargetingJNI")) {
044            try {
045                RuntimeLoader.loadLibrary(libraryName);
046                continue;
047            } catch (Exception e) {
048                System.out.println("Direct library load failed; falling back to extraction");
049            }
050
051            var nativeLibName = System.mapLibraryName(libraryName);
052            var path = "/nativelibraries/" + arch_name + "/" + nativeLibName;
053            var in = clazz.getResourceAsStream(path);
054
055            if (in == null) {
056                System.err.println("Could not get resource at path " + path);
057                return false;
058            }
059
060            // It's important that we don't mangle the names of these files on Windows at
061            // least
062            var tempfolder = Files.createTempDirectory("nativeextract");
063            File temp = new File(tempfolder.toAbsolutePath().toString(), nativeLibName);
064            System.out.println(temp.getAbsolutePath().toString());
065            FileOutputStream fos = new FileOutputStream(temp);
066
067            int read = -1;
068            byte[] buffer = new byte[1024];
069            while ((read = in.read(buffer)) != -1) {
070                fos.write(buffer, 0, read);
071            }
072            fos.close();
073            in.close();
074
075            try {
076                System.load(temp.getAbsolutePath());
077            } catch (Throwable t) {
078                System.err.println("Unable to System.load " + temp.getName() + " : " + t.getMessage());
079                t.printStackTrace();
080                return false;
081            }
082
083            System.out.println("Successfully loaded shared object " + temp.getName());
084        }
085
086        return true;
087    }
088}