zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

dll_windows.go (6545B)


      1 // Copyright 2015 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package gl
      6 
      7 import (
      8 	"archive/tar"
      9 	"compress/gzip"
     10 	"debug/pe"
     11 	"fmt"
     12 	"io"
     13 	"io/ioutil"
     14 	"log"
     15 	"net/http"
     16 	"os"
     17 	"path/filepath"
     18 	"runtime"
     19 )
     20 
     21 var debug = log.New(ioutil.Discard, "gl: ", log.LstdFlags)
     22 
     23 func downloadDLLs() (path string, err error) {
     24 	url := "https://dl.google.com/go/mobile/angle-bd3f8780b-" + runtime.GOARCH + ".tgz"
     25 	debug.Printf("downloading %s", url)
     26 	resp, err := http.Get(url)
     27 	if err != nil {
     28 		return "", fmt.Errorf("gl: %v", err)
     29 	}
     30 	defer func() {
     31 		err2 := resp.Body.Close()
     32 		if err == nil && err2 != nil {
     33 			err = fmt.Errorf("gl: error reading body from %v: %v", url, err2)
     34 		}
     35 	}()
     36 	if resp.StatusCode != http.StatusOK {
     37 		err := fmt.Errorf("gl: error fetching %v, status: %v", url, resp.Status)
     38 		return "", err
     39 	}
     40 
     41 	r, err := gzip.NewReader(resp.Body)
     42 	if err != nil {
     43 		return "", fmt.Errorf("gl: error reading gzip from %v: %v", url, err)
     44 	}
     45 	tr := tar.NewReader(r)
     46 	var bytesGLESv2, bytesEGL, bytesD3DCompiler []byte
     47 	for {
     48 		header, err := tr.Next()
     49 		if err == io.EOF {
     50 			break
     51 		}
     52 		if err != nil {
     53 			return "", fmt.Errorf("gl: error reading tar from %v: %v", url, err)
     54 		}
     55 		switch header.Name {
     56 		case "angle-" + runtime.GOARCH + "/libglesv2.dll":
     57 			bytesGLESv2, err = ioutil.ReadAll(tr)
     58 		case "angle-" + runtime.GOARCH + "/libegl.dll":
     59 			bytesEGL, err = ioutil.ReadAll(tr)
     60 		case "angle-" + runtime.GOARCH + "/d3dcompiler_47.dll":
     61 			bytesD3DCompiler, err = ioutil.ReadAll(tr)
     62 		default: // skip
     63 		}
     64 		if err != nil {
     65 			return "", fmt.Errorf("gl: error reading %v from %v: %v", header.Name, url, err)
     66 		}
     67 	}
     68 	if len(bytesGLESv2) == 0 || len(bytesEGL) == 0 || len(bytesD3DCompiler) == 0 {
     69 		return "", fmt.Errorf("gl: did not find all DLLs in %v", url)
     70 	}
     71 
     72 	writeDLLs := func(path string) error {
     73 		if err := ioutil.WriteFile(filepath.Join(path, "libglesv2.dll"), bytesGLESv2, 0755); err != nil {
     74 			return fmt.Errorf("gl: cannot install ANGLE: %v", err)
     75 		}
     76 		if err := ioutil.WriteFile(filepath.Join(path, "libegl.dll"), bytesEGL, 0755); err != nil {
     77 			return fmt.Errorf("gl: cannot install ANGLE: %v", err)
     78 		}
     79 		if err := ioutil.WriteFile(filepath.Join(path, "d3dcompiler_47.dll"), bytesD3DCompiler, 0755); err != nil {
     80 			return fmt.Errorf("gl: cannot install ANGLE: %v", err)
     81 		}
     82 		return nil
     83 	}
     84 
     85 	// First, we attempt to install these DLLs in LOCALAPPDATA/Shiny.
     86 	//
     87 	// Traditionally we would use the system32 directory, but it is
     88 	// no longer writable by normal programs.
     89 	os.MkdirAll(appdataPath(), 0775)
     90 	if err := writeDLLs(appdataPath()); err == nil {
     91 		return appdataPath(), nil
     92 	}
     93 	debug.Printf("DLLs could not be written to %s", appdataPath())
     94 
     95 	// Second, install in GOPATH/pkg if it exists.
     96 	gopath := os.Getenv("GOPATH")
     97 	gopathpkg := filepath.Join(gopath, "pkg")
     98 	if _, err := os.Stat(gopathpkg); err == nil && gopath != "" {
     99 		if err := writeDLLs(gopathpkg); err == nil {
    100 			return gopathpkg, nil
    101 		}
    102 	}
    103 	debug.Printf("DLLs could not be written to GOPATH")
    104 
    105 	// Third, pick a temporary directory.
    106 	tmp := os.TempDir()
    107 	if err := writeDLLs(tmp); err != nil {
    108 		return "", fmt.Errorf("gl: unable to install ANGLE DLLs: %v", err)
    109 	}
    110 	return tmp, nil
    111 }
    112 
    113 func appdataPath() string {
    114 	return filepath.Join(os.Getenv("LOCALAPPDATA"), "GoGL", runtime.GOARCH)
    115 }
    116 
    117 func containsDLLs(dir string) bool {
    118 	compatible := func(name string) bool {
    119 		file, err := pe.Open(filepath.Join(dir, name))
    120 		if err != nil {
    121 			return false
    122 		}
    123 		defer file.Close()
    124 
    125 		switch file.Machine {
    126 		case pe.IMAGE_FILE_MACHINE_AMD64:
    127 			return "amd64" == runtime.GOARCH
    128 		case pe.IMAGE_FILE_MACHINE_ARM:
    129 			return "arm" == runtime.GOARCH
    130 		case pe.IMAGE_FILE_MACHINE_I386:
    131 			return "386" == runtime.GOARCH
    132 		}
    133 		return false
    134 	}
    135 
    136 	return compatible("libglesv2.dll") && compatible("libegl.dll") && compatible("d3dcompiler_47.dll")
    137 }
    138 
    139 func chromePath() string {
    140 	// dlls are stored in:
    141 	//   <BASE>/<VERSION>/libglesv2.dll
    142 
    143 	var installdirs = []string{
    144 		// Chrome User
    145 		filepath.Join(os.Getenv("LOCALAPPDATA"), "Google", "Chrome", "Application"),
    146 		// Chrome System
    147 		filepath.Join(os.Getenv("ProgramFiles(x86)"), "Google", "Chrome", "Application"),
    148 		// Chromium
    149 		filepath.Join(os.Getenv("LOCALAPPDATA"), "Chromium", "Application"),
    150 		// Chrome Canary
    151 		filepath.Join(os.Getenv("LOCALAPPDATA"), "Google", "Chrome SxS", "Application"),
    152 	}
    153 
    154 	for _, installdir := range installdirs {
    155 		versiondirs, err := ioutil.ReadDir(installdir)
    156 		if err != nil {
    157 			continue
    158 		}
    159 
    160 		for _, versiondir := range versiondirs {
    161 			if !versiondir.IsDir() {
    162 				continue
    163 			}
    164 
    165 			versionpath := filepath.Join(installdir, versiondir.Name())
    166 			if containsDLLs(versionpath) {
    167 				return versionpath
    168 			}
    169 		}
    170 	}
    171 
    172 	return ""
    173 }
    174 
    175 func findDLLs() (err error) {
    176 	load := func(path string) (bool, error) {
    177 		if path != "" {
    178 			// don't try to start when one of the files is missing
    179 			if !containsDLLs(path) {
    180 				return false, nil
    181 			}
    182 
    183 			LibD3DCompiler.Name = filepath.Join(path, filepath.Base(LibD3DCompiler.Name))
    184 			LibGLESv2.Name = filepath.Join(path, filepath.Base(LibGLESv2.Name))
    185 			LibEGL.Name = filepath.Join(path, filepath.Base(LibEGL.Name))
    186 		}
    187 
    188 		if err := LibGLESv2.Load(); err == nil {
    189 			if err := LibEGL.Load(); err != nil {
    190 				return false, fmt.Errorf("gl: loaded libglesv2 but not libegl: %v", err)
    191 			}
    192 			if err := LibD3DCompiler.Load(); err != nil {
    193 				return false, fmt.Errorf("gl: loaded libglesv2, libegl but not d3dcompiler: %v", err)
    194 			}
    195 			if path == "" {
    196 				debug.Printf("DLLs found")
    197 			} else {
    198 				debug.Printf("DLLs found in: %q", path)
    199 			}
    200 			return true, nil
    201 		}
    202 
    203 		return false, nil
    204 	}
    205 
    206 	// Look in the system directory.
    207 	if ok, err := load(""); ok || err != nil {
    208 		return err
    209 	}
    210 
    211 	// Look in the AppData directory.
    212 	if ok, err := load(appdataPath()); ok || err != nil {
    213 		return err
    214 	}
    215 
    216 	// Look for a Chrome installation
    217 	if dir := chromePath(); dir != "" {
    218 		if ok, err := load(dir); ok || err != nil {
    219 			return err
    220 		}
    221 	}
    222 
    223 	// Look in GOPATH/pkg.
    224 	if ok, err := load(filepath.Join(os.Getenv("GOPATH"), "pkg")); ok || err != nil {
    225 		return err
    226 	}
    227 
    228 	// Look in temporary directory.
    229 	if ok, err := load(os.TempDir()); ok || err != nil {
    230 		return err
    231 	}
    232 
    233 	// Download the DLL binary.
    234 	path, err := downloadDLLs()
    235 	if err != nil {
    236 		return err
    237 	}
    238 	debug.Printf("DLLs written to %s", path)
    239 	if ok, err := load(path); !ok || err != nil {
    240 		return fmt.Errorf("gl: unable to load ANGLE after installation: %v", err)
    241 	}
    242 	return nil
    243 }