Class: Doom::WadDownloader

Inherits:
Object
  • Object
show all
Defined in:
lib/doom/wad_downloader.rb

Overview

Downloads the shareware DOOM1.WAD if not present

Defined Under Namespace

Classes: DownloadError

Constant Summary collapse

SHAREWARE_URL =

Doom shareware WAD v1.9 (freely distributable)

'https://raw.githubusercontent.com/Akbar30Bill/DOOM_wads/master/doom1.wad'
SHAREWARE_SIZE =

Expected size in bytes

4_196_020
SHAREWARE_FILENAME =
'doom1.wad'

Class Method Summary collapse

Class Method Details

.download_from_url(url, destination, redirect_limit: 5) ⇒ Object

Fetch a URL to a destination file, following redirects and streaming a progress bar. Retries without SSL verification if the certificate cannot be verified (a workaround for broken system cert stores) -- and because the check runs here, it applies to redirect targets too, not just the first URL.

Raises:



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/doom/wad_downloader.rb', line 83

def self.download_from_url(url, destination, redirect_limit: 5)
  raise DownloadError, 'Too many redirects' if redirect_limit.negative?

  uri = URI.parse(url)
  ssl_opts = { use_ssl: uri.scheme == 'https' }

  if ssl_opts[:use_ssl]
    begin
      Net::HTTP.start(uri.host, uri.port, **ssl_opts) { |h| h.head(uri) }
    rescue OpenSSL::SSL::SSLError
      puts 'SSL certificate verification failed, retrying without verification...'
      ssl_opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
    end
  end

  downloaded = 0
  Net::HTTP.start(uri.host, uri.port, **ssl_opts) do |http|
    request = Net::HTTP::Get.new(uri)

    http.request(request) do |response|
      case response
      when Net::HTTPRedirection
        return download_from_url(response['location'], destination,
                                 redirect_limit: redirect_limit - 1)
      when Net::HTTPSuccess
        total_size = response['content-length']&.to_i || SHAREWARE_SIZE

        File.open(destination, 'wb') do |file|
          response.read_body do |chunk|
            file.write(chunk)
            downloaded += chunk.size
            print_progress(downloaded, total_size)
          end
        end
      else
        raise DownloadError, "Download failed: #{response.code} #{response.message}"
      end
    end
  end
end

.download_shareware(destination) ⇒ Object

Raises:



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/doom/wad_downloader.rb', line 57

def self.download_shareware(destination)
  puts
  puts 'Downloading DOOM shareware...'

  # Create directory if needed
  FileUtils.mkdir_p(File.dirname(destination))

  download_from_url(SHAREWARE_URL, destination)

  puts
  puts "Downloaded to: #{destination}"
  puts

  # Verify file size
  actual_size = File.size(destination)
  return unless actual_size < 1_000_000 # Less than 1MB is suspicious

  File.delete(destination)
  raise DownloadError, "Download appears incomplete (#{actual_size} bytes)"
end

.ensure_wad_available(custom_path = nil) ⇒ Object

Raises:



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/doom/wad_downloader.rb', line 17

def self.ensure_wad_available(custom_path = nil)
  # If custom path provided and exists, use it
  return custom_path if custom_path && File.exist?(custom_path)

  # Check current directory for doom1.wad
  local_wad = File.join(Dir.pwd, SHAREWARE_FILENAME)
  return local_wad if File.exist?(local_wad)

  # Check home directory .doom folder
  home_wad = File.join(Dir.home, '.doom', SHAREWARE_FILENAME)
  return home_wad if File.exist?(home_wad)

  # No WAD found - offer to download
  raise DownloadError, "WAD file not found: #{custom_path}" if custom_path

  prompt_and_download(home_wad)
end


124
125
126
127
128
129
130
131
132
# File 'lib/doom/wad_downloader.rb', line 124

def self.print_progress(downloaded, total)
  percent = (downloaded.to_f / total * 100).to_i
  bar_width = 40
  filled = (percent * bar_width / 100)
  bar = ('=' * filled) + ('-' * (bar_width - filled))
  mb_downloaded = (downloaded / 1_048_576.0).round(1)
  mb_total = (total / 1_048_576.0).round(1)
  print "\r[#{bar}] #{percent}% (#{mb_downloaded}/#{mb_total} MB)"
end

.prompt_and_download(destination) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/doom/wad_downloader.rb', line 35

def self.prompt_and_download(destination)
  puts 'No DOOM WAD file found.'
  puts
  puts 'Would you like to download the shareware version of DOOM (4 MB)?'
  puts 'This is the free, legally distributable version with Episode 1.'
  puts
  print 'Download shareware DOOM? [Y/n] '

  response = $stdin.gets&.strip&.downcase
  if response.nil? || response.empty? || response == 'y' || response == 'yes'
    download_shareware(destination)
    destination
  else
    puts
    puts 'To play DOOM, you need a WAD file. Options:'
    puts "  1. Run 'doom' again and accept the shareware download"
    puts '  2. Copy your own doom1.wad or doom.wad to the current directory'
    puts '  3. Specify a WAD path: doom /path/to/your.wad'
    exit 1
  end
end